| 714 |
iacus |
1 |
/*
|
|
|
2 |
* R.app : a Cocoa front end to: "R A Computer Language for Statistical Data Analysis"
|
|
|
3 |
*
|
|
|
4 |
* R.app Copyright notes:
|
| 1254 |
iacus |
5 |
* Copyright (C) 2004-5 The R Foundation
|
| 714 |
iacus |
6 |
* written by Stefano M. Iacus and Simon Urbanek
|
|
|
7 |
*
|
|
|
8 |
*
|
|
|
9 |
* R Copyright notes:
|
|
|
10 |
* Copyright (C) 1995-1996 Robert Gentleman and Ross Ihaka
|
|
|
11 |
* Copyright (C) 1998-2001 The R Development Core Team
|
|
|
12 |
* Copyright (C) 2002-2004 The R Foundation
|
|
|
13 |
*
|
|
|
14 |
* This program is free software; you can redistribute it and/or modify
|
|
|
15 |
* it under the terms of the GNU General Public License as published by
|
|
|
16 |
* the Free Software Foundation; either version 2 of the License, or
|
|
|
17 |
* (at your option) any later version.
|
|
|
18 |
*
|
|
|
19 |
* This program is distributed in the hope that it will be useful,
|
|
|
20 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
22 |
* GNU General Public License for more details.
|
|
|
23 |
*
|
|
|
24 |
* A copy of the GNU General Public License is available via WWW at
|
|
|
25 |
* http://www.gnu.org/copyleft/gpl.html. You can also obtain it by
|
|
|
26 |
* writing to the Free Software Foundation, Inc., 59 Temple Place,
|
|
|
27 |
* Suite 330, Boston, MA 02111-1307 USA.
|
| 1362 |
urbaneks |
28 |
*
|
|
|
29 |
* $Id: RController.m 1603 2005-06-20 16:50:33Z urbaneks $
|
| 714 |
iacus |
30 |
*/
|
|
|
31 |
|
| 1045 |
urbaneks |
32 |
#import "RGUI.h"
|
| 928 |
urbaneks |
33 |
#include <R.h>
|
| 930 |
urbaneks |
34 |
#include <Rversion.h>
|
| 568 |
iacus |
35 |
#include <Rinternals.h>
|
|
|
36 |
#include <R_ext/Parse.h>
|
|
|
37 |
#include <Fileio.h>
|
| 930 |
urbaneks |
38 |
#if (R_VERSION >= R_Version(2,1,0))
|
|
|
39 |
// Rinterface is public, but present only in 2.1+
|
| 929 |
urbaneks |
40 |
#include <Rinterface.h>
|
| 930 |
urbaneks |
41 |
#else
|
|
|
42 |
// if there is no Rinterface, we need private Defn.h
|
|
|
43 |
#include <Defn.h>
|
|
|
44 |
#endif
|
| 1089 |
urbaneks |
45 |
#include <langinfo.h>
|
| 1136 |
urbaneks |
46 |
#include <locale.h>
|
| 1089 |
urbaneks |
47 |
|
| 568 |
iacus |
48 |
#import <sys/fcntl.h>
|
|
|
49 |
#import <sys/select.h>
|
|
|
50 |
#import <sys/types.h>
|
|
|
51 |
#import <sys/time.h>
|
| 688 |
urbaneks |
52 |
#import <sys/wait.h>
|
| 568 |
iacus |
53 |
#import <signal.h>
|
|
|
54 |
#import <unistd.h>
|
| 869 |
urbaneks |
55 |
#import "RController.h"
|
| 816 |
urbaneks |
56 |
#import "REngine/Rcallbacks.h"
|
|
|
57 |
#import "REngine/Rengine.h"
|
| 898 |
urbaneks |
58 |
#import "RConsoleTextStorage.h"
|
| 1420 |
urbaneks |
59 |
#import "RDocumentWinCtrl.h"
|
| 568 |
iacus |
60 |
|
| 816 |
urbaneks |
61 |
#import "Tools/Authorization.h"
|
| 1362 |
urbaneks |
62 |
|
| 815 |
urbaneks |
63 |
#import "Preferences.h"
|
| 796 |
urbaneks |
64 |
#import "SearchTable.h"
|
| 638 |
iacus |
65 |
|
| 568 |
iacus |
66 |
// size of the console output cache buffer
|
|
|
67 |
#define DEFAULT_WRITE_BUFFER_SIZE 32768
|
|
|
68 |
// 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.
|
|
|
69 |
#define writeBufferHighWaterMark (DEFAULT_WRITE_BUFFER_SIZE-4096)
|
|
|
70 |
// low water-mark of the buffer - if less than the water mark is available then the buffer will be flushed
|
|
|
71 |
#define writeBufferLowWaterMark 2048
|
|
|
72 |
|
|
|
73 |
/* RController.m: main GUI code originally based on Simon Urbanek's work of embedding R in Cocoa app (RGui?)
|
| 783 |
urbaneks |
74 |
The Code and File Completion is due to Simon U.
|
|
|
75 |
History handler is due to Simon U.
|
| 568 |
iacus |
76 |
*/
|
|
|
77 |
|
|
|
78 |
typedef struct {
|
| 783 |
urbaneks |
79 |
ParseStatus status;
|
|
|
80 |
int prompt_type;
|
|
|
81 |
int browselevel;
|
|
|
82 |
unsigned char buf[1025];
|
|
|
83 |
unsigned char *bufp;
|
| 568 |
iacus |
84 |
} R_ReplState;
|
|
|
85 |
|
|
|
86 |
extern R_ReplState state;
|
|
|
87 |
|
|
|
88 |
void run_Rmainloop(void); // from Rinit.c
|
|
|
89 |
extern void RGUI_ReplConsole(SEXP rho, int savestack, int browselevel); // from Rinit.c
|
|
|
90 |
extern int RGUI_ReplIteration(SEXP rho, int savestack, int browselevel, R_ReplState *state);
|
|
|
91 |
|
| 929 |
urbaneks |
92 |
// from Defn.h
|
|
|
93 |
|
|
|
94 |
int R_SetOptionWidth(int);
|
|
|
95 |
|
| 568 |
iacus |
96 |
#import "RController.h"
|
| 816 |
urbaneks |
97 |
#import "Tools/CodeCompletion.h"
|
|
|
98 |
#import "Tools/FileCompletion.h"
|
| 1136 |
urbaneks |
99 |
#import "HelpManager.h"
|
| 568 |
iacus |
100 |
#import "RDocument.h"
|
|
|
101 |
#import "PackageManager.h"
|
|
|
102 |
#import "DataManager.h"
|
|
|
103 |
#import "PackageInstaller.h"
|
|
|
104 |
#import "WSBrowser.h"
|
|
|
105 |
#import "HelpManager.h"
|
| 816 |
urbaneks |
106 |
#import "Quartz/RQuartz.h"
|
| 710 |
iacus |
107 |
#import "RDocumentController.h"
|
| 1212 |
iacus |
108 |
#import "SelectList.h"
|
| 568 |
iacus |
109 |
|
|
|
110 |
#import <unistd.h>
|
|
|
111 |
#import <sys/fcntl.h>
|
|
|
112 |
|
|
|
113 |
static RController* sharedRController;
|
|
|
114 |
|
|
|
115 |
@interface NSApplication (ScriptingSupport)
|
|
|
116 |
- (id)handleDCMDCommand:(NSScriptCommand*)command;
|
|
|
117 |
@end
|
|
|
118 |
|
|
|
119 |
@implementation NSApplication (ScriptingSupport)
|
|
|
120 |
- (id)handleDCMDCommand:(NSScriptCommand*)command
|
|
|
121 |
{
|
|
|
122 |
NSDictionary *args = [command evaluatedArguments];
|
|
|
123 |
NSString *cmd = [args objectForKey:@""];
|
|
|
124 |
if (!cmd || [cmd isEqualToString:@""])
|
|
|
125 |
return [NSNumber numberWithBool:NO];
|
| 1490 |
urbaneks |
126 |
[[RController sharedController] sendInput: cmd];
|
| 568 |
iacus |
127 |
return [NSNumber numberWithBool:YES];
|
|
|
128 |
}
|
|
|
129 |
@end
|
|
|
130 |
|
|
|
131 |
@implementation RController
|
|
|
132 |
|
|
|
133 |
- (id) init {
|
|
|
134 |
self = [super init];
|
|
|
135 |
|
|
|
136 |
runSystemAsRoot = NO;
|
|
|
137 |
toolbar = nil;
|
|
|
138 |
toolbarStopItem = nil;
|
|
|
139 |
rootFD = -1;
|
|
|
140 |
childPID = 0;
|
|
|
141 |
RLtimer = nil;
|
|
|
142 |
busyRFlag = YES;
|
| 1191 |
urbaneks |
143 |
appLaunched = NO;
|
| 575 |
urbaneks |
144 |
outputPosition = promptPosition = committedLength = 0;
|
| 568 |
iacus |
145 |
consoleInputQueue = [[NSMutableArray alloc] initWithCapacity:8];
|
|
|
146 |
currentConsoleInput = nil;
|
|
|
147 |
forceStdFlush = NO;
|
|
|
148 |
writeBufferLen = DEFAULT_WRITE_BUFFER_SIZE;
|
|
|
149 |
writeBufferPos = writeBuffer = (char*) malloc(writeBufferLen);
|
| 755 |
urbaneks |
150 |
readConsTransBufferSize = 1024; // initial size - will grow as needed
|
|
|
151 |
readConsTransBuffer = (char*) malloc(readConsTransBufferSize);
|
| 871 |
urbaneks |
152 |
textViewSync = [[NSString alloc] initWithString:@"consoleTextViewSemahphore"];
|
| 568 |
iacus |
153 |
|
| 815 |
urbaneks |
154 |
consoleColorsKeys = [[NSArray alloc] initWithObjects:
|
|
|
155 |
backgColorKey, inputColorKey, outputColorKey, promptColorKey,
|
|
|
156 |
stderrColorKey, stdoutColorKey, rootColorKey];
|
|
|
157 |
defaultConsoleColors = [[NSArray alloc] initWithObjects: // default colors
|
|
|
158 |
[NSColor whiteColor], [NSColor blueColor], [NSColor blackColor], [NSColor purpleColor],
|
|
|
159 |
[NSColor redColor], [NSColor grayColor], [NSColor purpleColor]];
|
|
|
160 |
consoleColors = [defaultConsoleColors mutableCopy];
|
|
|
161 |
|
| 898 |
urbaneks |
162 |
textFont = [[NSFont userFixedPitchFontOfSize:currentFontSize] retain];
|
| 568 |
iacus |
163 |
return self;
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
- (void) setRootFlag: (BOOL) flag
|
|
|
167 |
{
|
|
|
168 |
if (!flag) removeRootAuthorization();
|
|
|
169 |
runSystemAsRoot=flag;
|
|
|
170 |
|
|
|
171 |
{
|
|
|
172 |
NSArray * ia = [toolbar items];
|
|
|
173 |
int l = [ia count], i=0;
|
|
|
174 |
while (i<l) {
|
|
|
175 |
NSToolbarItem *ti = [ia objectAtIndex:i];
|
|
|
176 |
if ([[ti itemIdentifier] isEqual:AuthenticationToolbarItemIdentifier]) {
|
|
|
177 |
[ti setImage: [NSImage imageNamed: flag?@"lock-unlocked":@"lock-locked"]];
|
|
|
178 |
break;
|
|
|
179 |
}
|
|
|
180 |
i++;
|
|
|
181 |
}
|
|
|
182 |
}
|
|
|
183 |
}
|
|
|
184 |
|
|
|
185 |
- (BOOL) getRootFlag { return runSystemAsRoot; }
|
|
|
186 |
|
| 1328 |
urbaneks |
187 |
- (void) setRootFD: (int) fd {
|
|
|
188 |
rootFD=fd;
|
|
|
189 |
}
|
|
|
190 |
|
| 568 |
iacus |
191 |
- (NSFont*) currentFont
|
|
|
192 |
{
|
| 898 |
urbaneks |
193 |
return textFont;
|
| 568 |
iacus |
194 |
}
|
|
|
195 |
|
|
|
196 |
- (void) awakeFromNib {
|
| 1364 |
urbaneks |
197 |
char *args[4]={ "R", "--no-save", "--gui=cocoa", 0 };
|
| 1328 |
urbaneks |
198 |
SLog(@"RController.awakeFromNib");
|
| 1362 |
urbaneks |
199 |
|
| 568 |
iacus |
200 |
sharedRController = self;
|
| 747 |
urbaneks |
201 |
|
| 898 |
urbaneks |
202 |
NSLayoutManager *lm = [[RTextView layoutManager] retain];
|
|
|
203 |
NSTextStorage *origTS = [[RTextView textStorage] retain];
|
|
|
204 |
RConsoleTextStorage * textStorage = [[RConsoleTextStorage alloc] init];
|
|
|
205 |
[origTS removeLayoutManager:lm];
|
|
|
206 |
[textStorage addLayoutManager:lm];
|
|
|
207 |
[lm release];
|
|
|
208 |
[origTS release];
|
|
|
209 |
|
| 815 |
urbaneks |
210 |
[RConsoleWindow setBackgroundColor:[defaultConsoleColors objectAtIndex:iBackgroundColor]]; // we need this, because "update" doesn't touch the color if it's equal - and by default the window has *no* background - not even the default one, so we bring it in sync
|
| 1328 |
urbaneks |
211 |
[RConsoleWindow setOpaque:NO]; // Needed so we can see through it when we have clear stuff on top
|
|
|
212 |
[RTextView setDrawsBackground:NO];
|
|
|
213 |
[[RTextView enclosingScrollView] setDrawsBackground:NO];
|
| 815 |
urbaneks |
214 |
|
| 1328 |
urbaneks |
215 |
SLog(@" - load preferences");
|
| 1459 |
urbaneks |
216 |
[self updatePreferences]; // first update, then add self
|
| 815 |
urbaneks |
217 |
[[Preferences sharedPreferences] addDependent: self];
|
|
|
218 |
|
| 1328 |
urbaneks |
219 |
SLog(@" - init R_LIBS");
|
| 747 |
urbaneks |
220 |
{ // first initialize R_LIBS if necessary
|
| 815 |
urbaneks |
221 |
NSString *prefStr = [Preferences stringForKey:miscRAquaLibPathKey withDefault:nil];
|
| 747 |
urbaneks |
222 |
BOOL flag = !isAdmin(); // the default is YES for users and NO for admins
|
| 815 |
urbaneks |
223 |
if (prefStr)
|
|
|
224 |
flag=[prefStr isEqualToString: @"YES"];
|
| 747 |
urbaneks |
225 |
if (flag) {
|
|
|
226 |
char *cRLIBS = getenv("R_LIBS");
|
| 749 |
urbaneks |
227 |
NSString *addPath = [@"~/Library/R/library" stringByExpandingTildeInPath];
|
| 1460 |
urbaneks |
228 |
if (![[NSFileManager defaultManager] fileExistsAtPath:addPath]) { // make sure the directory exists
|
|
|
229 |
[[NSFileManager defaultManager] createDirectoryAtPath:[@"~/Library/R" stringByExpandingTildeInPath] attributes:nil];
|
|
|
230 |
[[NSFileManager defaultManager] createDirectoryAtPath:addPath attributes:nil];
|
|
|
231 |
}
|
| 747 |
urbaneks |
232 |
if (cRLIBS && *cRLIBS)
|
|
|
233 |
addPath = [NSString stringWithFormat: @"%s:%@", cRLIBS, addPath];
|
| 815 |
urbaneks |
234 |
setenv("R_LIBS", [addPath UTF8String], 1);
|
| 1328 |
urbaneks |
235 |
SLog(@" - setting R_LIBS=%s", [addPath UTF8String]);
|
| 747 |
urbaneks |
236 |
}
|
| 815 |
urbaneks |
237 |
}
|
| 1328 |
urbaneks |
238 |
SLog(@" - set APP VERSION");
|
| 749 |
urbaneks |
239 |
setenv("R_GUI_APP_VERSION", R_GUI_VERSION_STR, 1);
|
| 747 |
urbaneks |
240 |
|
| 1328 |
urbaneks |
241 |
SLog(@" - set R home");
|
|
|
242 |
if (!getenv("R_HOME")) {
|
|
|
243 |
NSBundle *rfb = [NSBundle bundleWithIdentifier:@"org.r-project.R-framework"];
|
| 1372 |
urbaneks |
244 |
if (!rfb) {
|
| 1328 |
urbaneks |
245 |
SLog(@" * problem: R_HOME is not set and I can't find the framework bundle");
|
| 1372 |
urbaneks |
246 |
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Library/Frameworks/R.framework/Resources/bin/R"]) {
|
|
|
247 |
SLog(@" * I'm being desperate and I found R at /Library/Frameworks/R.framework - so I'll use it, wish me luck");
|
|
|
248 |
setenv("R_HOME", "/Library/Frameworks/R.framework/Resources", 1);
|
|
|
249 |
} else
|
|
|
250 |
SLog(@" * I didn't even found R framework in the default location, I'm giving up - you're on your own");
|
|
|
251 |
} else {
|
| 1328 |
urbaneks |
252 |
SLog(@" %s", [[rfb resourcePath] UTF8String]);
|
|
|
253 |
setenv("R_HOME", [[rfb resourcePath] UTF8String], 1);
|
|
|
254 |
}
|
|
|
255 |
}
|
| 1477 |
urbaneks |
256 |
|
|
|
257 |
#if (R_VERSION >= R_Version(2,2,0))
|
|
|
258 |
if (!getenv("RGUI_NOWARN_RDEVEL"))
|
|
|
259 |
NSRunAlertPanel(@"Using R-devel (unstable!)",@"You are using R-devel (to become 2.2.0)\n\nSeveral properites that I rely on are changing in R-devel. I will take a wild guess as of how they change, but that guess can be completely wrong. Most importantly you have to use the default build settings. Proceed at your own risk.\n\n(Set RGUI_NOWARN_RDEVEL to get rid of this message)",NLS(@"OK"),nil,nil);
|
|
|
260 |
{
|
|
|
261 |
char tp[1024];
|
|
|
262 |
if (!getenv("R_INCLUDE_DIR")) {
|
|
|
263 |
strcpy(tp, getenv("R_HOME")); strcat(tp, "/include"); setenv("R_INCLUDE_DIR", tp, 1);
|
|
|
264 |
}
|
|
|
265 |
if (!getenv("R_SHARE_DIR")) {
|
|
|
266 |
strcpy(tp, getenv("R_HOME")); strcat(tp, "/share"); setenv("R_SHARE_DIR", tp, 1);
|
|
|
267 |
}
|
|
|
268 |
if (!getenv("R_DOC_DIR")) {
|
|
|
269 |
strcpy(tp, getenv("R_HOME")); strcat(tp, "/doc"); setenv("R_DOC_DIR", tp, 1);
|
|
|
270 |
}
|
|
|
271 |
}
|
|
|
272 |
#endif
|
| 1328 |
urbaneks |
273 |
|
|
|
274 |
/* setup LANG variable to match the system locale based on user's CFLocale */
|
| 908 |
urbaneks |
275 |
#if (R_VERSION >= R_Version(2,1,0))
|
| 1328 |
urbaneks |
276 |
SLog(@" - set locale");
|
| 1433 |
urbaneks |
277 |
if ([Preferences stringForKey:@"force.LANG"]) {
|
|
|
278 |
const char *ls = [[Preferences stringForKey:@"force.LANG"] UTF8String];
|
|
|
279 |
if (*ls) {
|
|
|
280 |
setenv("LANG", ls, 1);
|
|
|
281 |
SLog(@" - force.LANG present, setting LANG to \"%s\"", ls);
|
|
|
282 |
} else
|
|
|
283 |
SLog(@" - force.LANG present, but empty. LANG won't be set at all.");
|
|
|
284 |
} else if ([Preferences flagForKey:@"ignore.system.locale"]==YES) {
|
|
|
285 |
setenv("LANG", "en_US.UTF-8", 1);
|
|
|
286 |
SLog(@" - ignore.system.locale is set to YES, using en_US.UTF-8");
|
|
|
287 |
} else {
|
| 1089 |
urbaneks |
288 |
char cloc[64];
|
|
|
289 |
char *c = getenv("LANG");
|
|
|
290 |
cloc[63]=0;
|
|
|
291 |
if (c)
|
|
|
292 |
strcpy(cloc, c);
|
|
|
293 |
else {
|
|
|
294 |
CFLocaleRef lr = CFLocaleCopyCurrent();
|
|
|
295 |
CFStringRef ls = CFLocaleGetIdentifier(lr);
|
|
|
296 |
strncpy(cloc, [((NSString*)ls) UTF8String],63);
|
|
|
297 |
CFRelease(lr);
|
| 1048 |
urbaneks |
298 |
}
|
| 1089 |
urbaneks |
299 |
|
|
|
300 |
if (!strchr(cloc,'.'))
|
|
|
301 |
strcat(cloc,".UTF-8");
|
|
|
302 |
setenv("LANG", cloc, 1);
|
| 1328 |
urbaneks |
303 |
SLog(@" - setting LANG=%s", getenv("LANG"));
|
| 908 |
urbaneks |
304 |
}
|
|
|
305 |
#endif
|
| 1499 |
urbaneks |
306 |
|
|
|
307 |
BOOL noReenter = [Preferences flagForKey:@"REngine prevent reentrance"];
|
|
|
308 |
if (noReenter == YES) preventReentrance = YES;
|
| 908 |
urbaneks |
309 |
|
| 1328 |
urbaneks |
310 |
SLog(@" - init R");
|
| 688 |
urbaneks |
311 |
[[[REngine alloc] initWithHandler:self arguments:args] setCocoaHandler:self];
|
| 1328 |
urbaneks |
312 |
|
|
|
313 |
SLog(@" - font and other widgets");
|
| 898 |
urbaneks |
314 |
/*
|
| 568 |
iacus |
315 |
[RTextView setFont:[NSFont userFixedPitchFontOfSize:currentFontSize]];
|
|
|
316 |
[fontSizeStepper setIntValue:currentFontSize];
|
|
|
317 |
theFont=[RTextView font];
|
|
|
318 |
theFont=[[NSFontManager sharedFontManager] convertFont:theFont toSize:[fontSizeStepper intValue]];
|
|
|
319 |
[RTextView setFont:theFont];
|
| 898 |
urbaneks |
320 |
*/
|
|
|
321 |
[RTextView setFont: textFont];
|
| 568 |
iacus |
322 |
{
|
|
|
323 |
NSMutableDictionary *md = [[RTextView typingAttributes] mutableCopy];
|
| 815 |
urbaneks |
324 |
[md setObject: [consoleColors objectAtIndex:iInputColor] forKey: @"NSColor"];
|
| 568 |
iacus |
325 |
[RTextView setTypingAttributes:[NSDictionary dictionaryWithDictionary:md]];
|
|
|
326 |
[md release];
|
|
|
327 |
}
|
| 590 |
urbaneks |
328 |
[RTextView setContinuousSpellCheckingEnabled:NO]; // force 'no spell checker'
|
| 1488 |
urbaneks |
329 |
[[RTextView textStorage] setDelegate:self];
|
| 899 |
urbaneks |
330 |
|
| 783 |
urbaneks |
331 |
// [RTextView changeColor: inputColor];
|
| 568 |
iacus |
332 |
[RTextView display];
|
|
|
333 |
[self setupToolbar];
|
| 815 |
urbaneks |
334 |
|
| 568 |
iacus |
335 |
[ RConsoleWindow setDocumentEdited:YES];
|
|
|
336 |
|
|
|
337 |
//NSLog(@"RController: awake: done");
|
| 1328 |
urbaneks |
338 |
|
|
|
339 |
SLog(@" - setup timer");
|
| 568 |
iacus |
340 |
WDirtimer = [NSTimer scheduledTimerWithTimeInterval:0.5
|
| 575 |
urbaneks |
341 |
target:self
|
|
|
342 |
selector:@selector(showWorkingDir:)
|
|
|
343 |
userInfo:0
|
|
|
344 |
repeats:YES];
|
| 568 |
iacus |
345 |
|
|
|
346 |
hist=[[History alloc] init];
|
| 783 |
urbaneks |
347 |
|
|
|
348 |
|
| 568 |
iacus |
349 |
BOOL WantThread = YES;
|
|
|
350 |
|
| 1328 |
urbaneks |
351 |
SLog(@" - setup stdout/err grabber");
|
| 568 |
iacus |
352 |
if(WantThread){ // re-route the stdout to our own file descriptor and use ConnectionCache on it
|
|
|
353 |
int pfd[2];
|
|
|
354 |
pipe(pfd);
|
|
|
355 |
dup2(pfd[1], STDOUT_FILENO);
|
|
|
356 |
close(pfd[1]);
|
|
|
357 |
|
|
|
358 |
stdoutFD=pfd[0];
|
| 783 |
urbaneks |
359 |
|
| 568 |
iacus |
360 |
pipe(pfd);
|
|
|
361 |
#ifndef PLAIN_STDERR
|
|
|
362 |
dup2(pfd[1], STDERR_FILENO);
|
|
|
363 |
close(pfd[1]);
|
|
|
364 |
#endif
|
|
|
365 |
|
|
|
366 |
stderrFD=pfd[0];
|
| 783 |
urbaneks |
367 |
|
| 568 |
iacus |
368 |
[self addConnectionLog];
|
|
|
369 |
}
|
| 783 |
urbaneks |
370 |
|
| 1328 |
urbaneks |
371 |
SLog(@" - set cwd and load history");
|
| 568 |
iacus |
372 |
[historyView setDoubleAction: @selector(historyDoubleClick:)];
|
| 783 |
urbaneks |
373 |
|
| 568 |
iacus |
374 |
currentSize = [[RTextView textContainer] containerSize].width;
|
|
|
375 |
//currentFontSize = [[RTextView font] pointSize];
|
|
|
376 |
currentConsoleWidth = -1;
|
| 1191 |
urbaneks |
377 |
[[NSFileManager defaultManager] changeCurrentDirectoryPath: [[Preferences stringForKey:initialWorkingDirectoryKey withDefault:@"~"] stringByExpandingTildeInPath]];
|
| 1136 |
urbaneks |
378 |
if ([Preferences flagForKey:importOnStartupKey withDefault:YES]) {
|
| 1191 |
urbaneks |
379 |
[self doLoadHistory:nil];
|
| 1136 |
urbaneks |
380 |
}
|
| 1328 |
urbaneks |
381 |
SLog(@" - awake is done");
|
| 568 |
iacus |
382 |
}
|
|
|
383 |
|
|
|
384 |
-(void) applicationDidFinishLaunching: (NSNotification *)aNotification
|
|
|
385 |
{
|
| 1328 |
urbaneks |
386 |
SLog(@"RController.applicationDidFinishLaunching");
|
|
|
387 |
SLog(@" - initalizing R");
|
| 568 |
iacus |
388 |
if (![[REngine mainEngine] activate]) {
|
| 1045 |
urbaneks |
389 |
NSRunAlertPanel(NLS(@"Cannot start R"),[NSString stringWithFormat:NLS(@"Unable to start R: %@"), [[REngine mainEngine] lastError]],NLS(@"OK"),nil,nil);
|
| 568 |
iacus |
390 |
exit(-1);
|
|
|
391 |
}
|
| 783 |
urbaneks |
392 |
|
| 1328 |
urbaneks |
393 |
SLog(@" - set R options");
|
| 1146 |
urbaneks |
394 |
// force html-help, because that's the only format we can handle ATM
|
|
|
395 |
[[REngine mainEngine] executeString: @"options(htmlhelp=TRUE)"];
|
| 1396 |
urbaneks |
396 |
|
|
|
397 |
SLog(@" - set default CRAN mirror");
|
|
|
398 |
{
|
|
|
399 |
NSString *url = [Preferences stringForKey:defaultCRANmirrorURLKey withDefault:@""];
|
|
|
400 |
if (![url isEqualToString:@""])
|
|
|
401 |
[[REngine mainEngine] executeString:[NSString stringWithFormat:@"try({ r <- getOption('repos'); r['CRAN']<-gsub('/$', '', \"%@\"); options(repos = r) },silent=TRUE)", url]];
|
|
|
402 |
}
|
|
|
403 |
|
| 1328 |
urbaneks |
404 |
SLog(@" - clean up and flush console");
|
| 568 |
iacus |
405 |
[self setOptionWidth:YES];
|
|
|
406 |
[RTextView setEditable:YES];
|
| 575 |
urbaneks |
407 |
[self flushROutput];
|
| 568 |
iacus |
408 |
|
| 1328 |
urbaneks |
409 |
SLog(@" - setup notification and timers");
|
| 568 |
iacus |
410 |
[[NSNotificationCenter defaultCenter]
|
|
|
411 |
addObserver:self
|
|
|
412 |
selector:@selector(RConsoleDidResize:)
|
|
|
413 |
name:NSWindowDidResizeNotification
|
|
|
414 |
object: RConsoleWindow];
|
|
|
415 |
|
| 575 |
urbaneks |
416 |
timer = [NSTimer scheduledTimerWithTimeInterval:0.05
|
|
|
417 |
target:self
|
|
|
418 |
selector:@selector(otherEventLoops:)
|
|
|
419 |
userInfo:0
|
|
|
420 |
repeats:YES];
|
|
|
421 |
Flushtimer = [NSTimer scheduledTimerWithTimeInterval:0.5
|
|
|
422 |
target:self
|
|
|
423 |
selector:@selector(flushTimerHook:)
|
|
|
424 |
userInfo:0
|
|
|
425 |
repeats:YES];
|
| 783 |
urbaneks |
426 |
|
| 899 |
urbaneks |
427 |
// once we're ready with the doc transition, the following will actually fire up the cconsole window
|
|
|
428 |
//[[NSDocumentController sharedDocumentController] openUntitledDocumentOfType:@"Rcommand" display:YES];
|
|
|
429 |
|
| 1328 |
urbaneks |
430 |
SLog(@" - show main window");
|
| 899 |
urbaneks |
431 |
[RConsoleWindow makeKeyAndOrderFront:self];
|
| 904 |
urbaneks |
432 |
//[[REngine mainEngine] runDelayedREPL]; // start delayed REPL
|
| 899 |
urbaneks |
433 |
//CGPostKeyboardEvent(27, 53, 1); // post <ESC> to cause SIGINT and thus the actual start of REPL
|
|
|
434 |
|
| 1328 |
urbaneks |
435 |
SLog(@" - setup REPL trigger timer");
|
| 568 |
iacus |
436 |
if (!RLtimer)
|
|
|
437 |
RLtimer = [NSTimer scheduledTimerWithTimeInterval:0.001
|
|
|
438 |
target:self
|
| 871 |
urbaneks |
439 |
selector:@selector(kickstart:)
|
| 568 |
iacus |
440 |
userInfo:0
|
| 1191 |
urbaneks |
441 |
repeats:NO];
|
|
|
442 |
appLaunched = YES;
|
| 1488 |
urbaneks |
443 |
|
| 1328 |
urbaneks |
444 |
SLog(@" - done, ready to go");
|
| 568 |
iacus |
445 |
}
|
|
|
446 |
|
| 1257 |
urbaneks |
447 |
- (BOOL)appLaunched {
|
|
|
448 |
return appLaunched;
|
|
|
449 |
}
|
|
|
450 |
|
| 871 |
urbaneks |
451 |
- (void) kickstart:(id) sender {
|
| 904 |
urbaneks |
452 |
//kill(getpid(),SIGINT);
|
| 1488 |
urbaneks |
453 |
[self setStatusLineText:@""];
|
| 904 |
urbaneks |
454 |
[[REngine mainEngine] runREPL];
|
| 871 |
urbaneks |
455 |
}
|
|
|
456 |
|
| 568 |
iacus |
457 |
-(void) addConnectionLog
|
|
|
458 |
{
|
|
|
459 |
NSPort *port1;
|
|
|
460 |
NSPort *port2;
|
|
|
461 |
NSArray *portArray;
|
|
|
462 |
NSConnection *connectionToTransferServer;
|
|
|
463 |
|
|
|
464 |
port1 = [NSPort port];
|
|
|
465 |
port2 = [NSPort port];
|
|
|
466 |
connectionToTransferServer = [[NSConnection alloc] initWithReceivePort:port1 sendPort:port2];
|
|
|
467 |
[connectionToTransferServer setRootObject:self];
|
|
|
468 |
|
|
|
469 |
portArray = [NSArray arrayWithObjects:port2, port1, nil];
|
|
|
470 |
[NSThread detachNewThreadSelector:@selector(readThread:)
|
|
|
471 |
toTarget:self
|
|
|
472 |
withObject:portArray];
|
|
|
473 |
}
|
|
|
474 |
|
|
|
475 |
- (void) readThread: (NSArray *)portArray
|
|
|
476 |
{
|
|
|
477 |
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
|
|
|
478 |
NSConnection *connectionToController;
|
|
|
479 |
RController *rc;
|
|
|
480 |
unsigned int bufSize=2048;
|
| 871 |
urbaneks |
481 |
char *buf=(char*) malloc(bufSize+16);
|
| 587 |
urbaneks |
482 |
int n=0, pib=0, flushMark=bufSize-(bufSize>>2);
|
| 568 |
iacus |
483 |
int bufFD=0;
|
|
|
484 |
fd_set readfds;
|
|
|
485 |
struct timeval timv;
|
| 783 |
urbaneks |
486 |
|
| 568 |
iacus |
487 |
timv.tv_sec=0; timv.tv_usec=300000; /* timeout */
|
| 783 |
urbaneks |
488 |
|
| 568 |
iacus |
489 |
connectionToController = [NSConnection connectionWithReceivePort:[portArray objectAtIndex:0]
|
|
|
490 |
sendPort:[portArray objectAtIndex:1]];
|
| 783 |
urbaneks |
491 |
|
| 568 |
iacus |
492 |
rc = ((RController *)[connectionToController rootProxy]);
|
|
|
493 |
|
|
|
494 |
fcntl(stdoutFD, F_SETFL, O_NONBLOCK);
|
|
|
495 |
fcntl(stderrFD, F_SETFL, O_NONBLOCK);
|
|
|
496 |
while (1) {
|
|
|
497 |
int selr=0, maxfd=stdoutFD;
|
| 1328 |
urbaneks |
498 |
int validRootFD=-1;
|
| 568 |
iacus |
499 |
FD_ZERO(&readfds);
|
|
|
500 |
FD_SET(stdoutFD,&readfds);
|
|
|
501 |
FD_SET(stderrFD,&readfds); if(stderrFD>maxfd) maxfd=stderrFD;
|
| 1328 |
urbaneks |
502 |
if (rootFD!=-1) {
|
|
|
503 |
validRootFD = rootFD; // we copy it as to not run into threading problems when it is changed while we're in the middle of processing
|
|
|
504 |
FD_SET(rootFD,&readfds);
|
|
|
505 |
if(rootFD>maxfd) maxfd=rootFD;
|
|
|
506 |
}
|
| 568 |
iacus |
507 |
selr=select(maxfd+1, &readfds, 0, 0, &timv);
|
|
|
508 |
if (FD_ISSET(stdoutFD, &readfds)) {
|
|
|
509 |
if (bufFD!=0 && pib>0) {
|
| 871 |
urbaneks |
510 |
@try{
|
|
|
511 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
|
|
512 |
} @catch(NSException *ex) {
|
|
|
513 |
}
|
| 568 |
iacus |
514 |
pib=0;
|
|
|
515 |
}
|
|
|
516 |
bufFD=0;
|
|
|
517 |
while (pib<bufSize && (n=read(stdoutFD,buf+pib,bufSize-pib))>0)
|
|
|
518 |
pib+=n;
|
|
|
519 |
if (pib>flushMark) { // if we reach the flush mark, dump it
|
| 871 |
urbaneks |
520 |
@try{
|
| 568 |
iacus |
521 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
| 871 |
urbaneks |
522 |
} @catch(NSException *ex) {
|
|
|
523 |
}
|
| 568 |
iacus |
524 |
pib=0;
|
|
|
525 |
}
|
|
|
526 |
}
|
|
|
527 |
if (FD_ISSET(stderrFD, &readfds)) {
|
|
|
528 |
if (bufFD!=1 && pib>0) {
|
|
|
529 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
|
|
530 |
pib=0;
|
|
|
531 |
}
|
|
|
532 |
bufFD=1;
|
|
|
533 |
while (pib<bufSize && (n=read(stderrFD,buf+pib,bufSize-pib))>0)
|
|
|
534 |
pib+=n;
|
|
|
535 |
if (pib>flushMark) { // if we reach the flush mark, dump it
|
| 871 |
urbaneks |
536 |
@try{
|
| 568 |
iacus |
537 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
| 871 |
urbaneks |
538 |
} @catch(NSException *ex) {
|
|
|
539 |
}
|
| 568 |
iacus |
540 |
pib=0;
|
|
|
541 |
}
|
|
|
542 |
}
|
| 1328 |
urbaneks |
543 |
if (validRootFD!=-1 && FD_ISSET(validRootFD, &readfds)) {
|
| 568 |
iacus |
544 |
if (bufFD!=2 && pib>0) {
|
| 871 |
urbaneks |
545 |
@try{
|
|
|
546 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
|
|
547 |
} @catch(NSException *ex) {
|
|
|
548 |
}
|
| 568 |
iacus |
549 |
pib=0;
|
|
|
550 |
}
|
|
|
551 |
bufFD=2;
|
| 1328 |
urbaneks |
552 |
while (pib<bufSize && (n=read(validRootFD,buf+pib,bufSize-pib))>0)
|
| 568 |
iacus |
553 |
pib+=n;
|
|
|
554 |
if (n==0 || pib>flushMark) { // if we reach the flush mark, dump it
|
| 871 |
urbaneks |
555 |
@try{
|
| 568 |
iacus |
556 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
| 871 |
urbaneks |
557 |
} @catch(NSException *ex) {
|
|
|
558 |
}
|
| 568 |
iacus |
559 |
pib=0;
|
|
|
560 |
}
|
| 1328 |
urbaneks |
561 |
if (n==0) rootFD=-1; // we indicate EOF on the rootFD by setting it to -1
|
| 568 |
iacus |
562 |
}
|
|
|
563 |
if ((forceStdFlush || selr==0) && pib>0) { // dump also if we got a timeout
|
| 871 |
urbaneks |
564 |
@try{
|
| 568 |
iacus |
565 |
[rc writeLogsWithBytes:buf length:pib type:bufFD];
|
| 871 |
urbaneks |
566 |
} @catch(NSException *ex) {
|
|
|
567 |
}
|
| 568 |
iacus |
568 |
pib=0;
|
|
|
569 |
}
|
|
|
570 |
}
|
|
|
571 |
free(buf);
|
|
|
572 |
|
|
|
573 |
[pool release];
|
|
|
574 |
}
|
|
|
575 |
|
|
|
576 |
- (void) flushStdConsole
|
|
|
577 |
{
|
|
|
578 |
fflush(stderr);
|
|
|
579 |
fflush(stdout);
|
| 575 |
urbaneks |
580 |
forceStdFlush=YES;
|
| 568 |
iacus |
581 |
}
|
|
|
582 |
|
|
|
583 |
- (void) addChildProcess: (pid_t) pid
|
|
|
584 |
{
|
|
|
585 |
childPID=pid;
|
|
|
586 |
if (pid>0 && toolbarStopItem) [toolbarStopItem setEnabled:YES];
|
|
|
587 |
}
|
|
|
588 |
|
|
|
589 |
- (void) rmChildProcess: (pid_t) pid
|
|
|
590 |
{
|
|
|
591 |
childPID=0;
|
|
|
592 |
if (!busyRFlag && toolbarStopItem) [toolbarStopItem setEnabled:NO];
|
| 575 |
urbaneks |
593 |
[self flushStdConsole];
|
| 568 |
iacus |
594 |
}
|
|
|
595 |
|
|
|
596 |
// When you have the toolbar in text-only mode, this action is called
|
|
|
597 |
// by the toolbar item's menu to make the font bigger. We just change the stepper control and
|
|
|
598 |
// call our -changeFontSize: action.
|
|
|
599 |
-(IBAction) fontSizeBigger:(id)sender
|
|
|
600 |
{
|
|
|
601 |
if ([RConsoleWindow isKeyWindow]) {
|
|
|
602 |
[fontSizeStepper setIntValue:[fontSizeStepper intValue]+1];
|
|
|
603 |
[self changeFontSize:NULL];
|
|
|
604 |
} else
|
|
|
605 |
[[NSFontManager sharedFontManager] modifyFont:sender];
|
|
|
606 |
}
|
|
|
607 |
|
|
|
608 |
// When you have the toolbar in text-only mode, this action is called
|
|
|
609 |
// by the toolbar item's menu to make the font smaller. We just change the stepper control and
|
|
|
610 |
// call our -changeFontSize: action.
|
|
|
611 |
-(IBAction) fontSizeSmaller:(id)sender
|
|
|
612 |
{
|
|
|
613 |
if ([RConsoleWindow isKeyWindow]) {
|
|
|
614 |
[fontSizeStepper setIntValue:[fontSizeStepper intValue]-1];
|
|
|
615 |
[self changeFontSize:NULL];
|
|
|
616 |
} else
|
|
|
617 |
[[NSFontManager sharedFontManager] modifyFont:sender];
|
| 783 |
urbaneks |
618 |
|
| 568 |
iacus |
619 |
}
|
|
|
620 |
|
|
|
621 |
// This action is called to change the font size. It's called by the NSPopUpButton in the toolbar item's
|
|
|
622 |
// custom view, and also by the above routines called from the toolbar item's menu (in text-only mode).
|
|
|
623 |
-(IBAction) changeFontSize:(id)sender
|
|
|
624 |
{
|
|
|
625 |
NSFont *theFont;
|
|
|
626 |
|
|
|
627 |
[fontSizeField takeIntValueFrom:fontSizeStepper];
|
|
|
628 |
theFont=[RTextView font];
|
|
|
629 |
theFont=[[NSFontManager sharedFontManager] convertFont:theFont toSize:[fontSizeStepper intValue]];
|
|
|
630 |
[RTextView setFont:theFont];
|
|
|
631 |
[self setOptionWidth:NO];
|
|
|
632 |
[[NSUserDefaults standardUserDefaults] setFloat: [fontSizeStepper intValue] forKey:FontSizeKey];
|
|
|
633 |
}
|
|
|
634 |
|
|
|
635 |
|
|
|
636 |
extern BOOL isTimeToFinish;
|
|
|
637 |
|
|
|
638 |
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app {
|
|
|
639 |
if (![self windowShouldClose:self])
|
|
|
640 |
return NSTerminateCancel;
|
|
|
641 |
|
|
|
642 |
if(timer){
|
|
|
643 |
[timer invalidate];
|
|
|
644 |
timer = nil;
|
|
|
645 |
}
|
| 783 |
urbaneks |
646 |
|
| 568 |
iacus |
647 |
if(RLtimer){
|
|
|
648 |
[RLtimer invalidate];
|
|
|
649 |
RLtimer = nil;
|
|
|
650 |
}
|
| 783 |
urbaneks |
651 |
|
| 568 |
iacus |
652 |
if(Flushtimer){
|
|
|
653 |
[Flushtimer invalidate];
|
|
|
654 |
Flushtimer = nil;
|
|
|
655 |
}
|
|
|
656 |
|
|
|
657 |
if(WDirtimer){
|
|
|
658 |
[WDirtimer invalidate];
|
|
|
659 |
WDirtimer = nil;
|
|
|
660 |
}
|
| 783 |
urbaneks |
661 |
|
| 568 |
iacus |
662 |
return NSTerminateNow;
|
|
|
663 |
}
|
|
|
664 |
|
|
|
665 |
|
|
|
666 |
- (void) dealloc {
|
|
|
667 |
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
| 815 |
urbaneks |
668 |
[[Preferences sharedPreferences] removeDependent:self];
|
|
|
669 |
[defaultConsoleColors release];
|
|
|
670 |
[consoleColors release];
|
|
|
671 |
[consoleColorsKeys release];
|
| 568 |
iacus |
672 |
[super dealloc];
|
|
|
673 |
}
|
|
|
674 |
|
| 575 |
urbaneks |
675 |
- (void) flushTimerHook: (NSTimer*) source
|
|
|
676 |
{
|
|
|
677 |
[self flushROutput];
|
|
|
678 |
}
|
|
|
679 |
|
|
|
680 |
- (void) flushROutput {
|
| 568 |
iacus |
681 |
if (writeBuffer!=writeBufferPos) {
|
| 815 |
urbaneks |
682 |
[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:[consoleColors objectAtIndex:iOutputColor]];
|
| 568 |
iacus |
683 |
writeBufferPos=writeBuffer;
|
|
|
684 |
}
|
| 575 |
urbaneks |
685 |
}
|
|
|
686 |
|
|
|
687 |
- (void) handleFlushConsole {
|
|
|
688 |
[self flushROutput];
|
| 568 |
iacus |
689 |
[self flushStdConsole];
|
|
|
690 |
}
|
|
|
691 |
|
|
|
692 |
/* this writes R output to the Console window, but indirectly by using a buffer */
|
|
|
693 |
- (void) handleWriteConsole: (NSString*) txt {
|
|
|
694 |
const char *s = [txt UTF8String];
|
|
|
695 |
int sl = strlen(s);
|
|
|
696 |
int fits = writeBufferLen-(writeBufferPos-writeBuffer)-1;
|
|
|
697 |
|
|
|
698 |
// let's flush the buffer if the new string is large and it would, but the buffer should be occupied
|
|
|
699 |
if (fits<sl && fits>writeBufferHighWaterMark) {
|
| 575 |
urbaneks |
700 |
// for efficiency we're not using handleFlushConsole, because that would trigger stdxx flush, too
|
| 815 |
urbaneks |
701 |
[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:[consoleColors objectAtIndex:iOutputColor]];
|
| 568 |
iacus |
702 |
writeBufferPos=writeBuffer;
|
|
|
703 |
fits = writeBufferLen-1;
|
|
|
704 |
}
|
|
|
705 |
|
|
|
706 |
while (fits<sl) { // ok, we're in a situation where we must split the string
|
|
|
707 |
memcpy(writeBufferPos, s, fits);
|
|
|
708 |
writeBufferPos[writeBufferLen-1]=0;
|
| 815 |
urbaneks |
709 |
[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:[consoleColors objectAtIndex:iOutputColor]];
|
| 568 |
iacus |
710 |
sl-=fits; s+=fits;
|
|
|
711 |
writeBufferPos=writeBuffer;
|
|
|
712 |
fits=writeBufferLen-1;
|
|
|
713 |
}
|
| 783 |
urbaneks |
714 |
|
| 568 |
iacus |
715 |
strcpy(writeBufferPos, s);
|
|
|
716 |
writeBufferPos+=sl;
|
|
|
717 |
|
|
|
718 |
// flush the buffer if the low watermark is reached
|
|
|
719 |
if (fits-sl<writeBufferLowWaterMark) {
|
| 815 |
urbaneks |
720 |
[self writeConsoleDirectly:[NSString stringWithUTF8String:writeBuffer] withColor:[consoleColors objectAtIndex:iOutputColor]];
|
| 568 |
iacus |
721 |
writeBufferPos=writeBuffer;
|
|
|
722 |
}
|
|
|
723 |
}
|
|
|
724 |
|
|
|
725 |
/* this writes R output to the Console window directly, i.e. without using a buffer. Use handleWriteConsole: for the regular way. */
|
| 575 |
urbaneks |
726 |
- (void) writeConsoleDirectly: (NSString*) txt withColor: (NSColor*) color{
|
| 871 |
urbaneks |
727 |
@synchronized(textViewSync) {
|
| 898 |
urbaneks |
728 |
RConsoleTextStorage *textStorage = (RConsoleTextStorage*) [RTextView textStorage];
|
| 871 |
urbaneks |
729 |
NSRange origSel = [RTextView selectedRange];
|
|
|
730 |
unsigned tl = [txt length];
|
|
|
731 |
if (tl>0) {
|
|
|
732 |
unsigned oldCL=committedLength;
|
|
|
733 |
/* NSLog(@"original: %d:%d, insertion: %d, length: %d, prompt: %d, commit: %d", origSel.location,
|
|
|
734 |
origSel.length, outputPosition, tl, promptPosition, committedLength); */
|
| 898 |
urbaneks |
735 |
[textStorage beginEditing];
|
| 871 |
urbaneks |
736 |
committedLength=0;
|
| 898 |
urbaneks |
737 |
[textStorage insertText:txt atIndex:outputPosition withColor:color];
|
| 871 |
urbaneks |
738 |
if (outputPosition<=promptPosition) promptPosition+=tl;
|
|
|
739 |
committedLength=oldCL;
|
|
|
740 |
if (outputPosition<=committedLength) committedLength+=tl;
|
|
|
741 |
if (outputPosition<=origSel.location) origSel.location+=tl;
|
|
|
742 |
outputPosition+=tl;
|
| 898 |
urbaneks |
743 |
[textStorage endEditing];
|
| 871 |
urbaneks |
744 |
[RTextView setSelectedRange:origSel];
|
| 898 |
urbaneks |
745 |
[RTextView scrollRangeToVisible:origSel];
|
| 871 |
urbaneks |
746 |
}
|
| 575 |
urbaneks |
747 |
}
|
| 568 |
iacus |
748 |
}
|
|
|
749 |
|
|
|
750 |
/* Just writes the prompt in a different color */
|
|
|
751 |
- (void)handleWritePrompt: (NSString*) prompt {
|
|
|
752 |
[self handleFlushConsole];
|
| 871 |
urbaneks |
753 |
@synchronized(textViewSync) {
|
| 898 |
urbaneks |
754 |
RConsoleTextStorage *textStorage = (RConsoleTextStorage*) [RTextView textStorage];
|
|
|
755 |
unsigned textLength = [textStorage length];
|
| 871 |
urbaneks |
756 |
int promptLength=[prompt length];
|
| 1257 |
urbaneks |
757 |
// NSLog(@"Prompt: %@", prompt);
|
| 898 |
urbaneks |
758 |
NSRange lr = [[textStorage string] lineRangeForRange:NSMakeRange(textLength,0)];
|
|
|
759 |
[textStorage beginEditing];
|
| 871 |
urbaneks |
760 |
promptPosition=textLength;
|
|
|
761 |
if (lr.location!=textLength) { // the prompt must be on the beginning of the line
|
| 898 |
urbaneks |
762 |
[textStorage insertText: @"\n" atIndex: textLength withColor:[consoleColors objectAtIndex:iPromptColor]];
|
|
|
763 |
textLength = [textStorage length];
|
|
|
764 |
promptLength++;
|
| 871 |
urbaneks |
765 |
}
|
|
|
766 |
|
|
|
767 |
if (promptLength>0) {
|
| 898 |
urbaneks |
768 |
[textStorage insertText:prompt atIndex: textLength withColor:[consoleColors objectAtIndex:iPromptColor]];
|
| 871 |
urbaneks |
769 |
if (promptLength>1) // this is a trick to make sure that the insertion color doesn't change at the prompt
|
| 898 |
urbaneks |
770 |
[textStorage addAttribute:@"NSColor" value:[consoleColors objectAtIndex:iInputColor] range:NSMakeRange(promptPosition+promptLength-1, 1)];
|
|
|
771 |
committedLength=promptPosition+promptLength;
|
| 871 |
urbaneks |
772 |
}
|
|
|
773 |
committedLength=promptPosition+promptLength;
|
| 898 |
urbaneks |
774 |
[textStorage endEditing];
|
|
|
775 |
{
|
|
|
776 |
NSRange targetRange = NSMakeRange(committedLength,0);
|
|
|
777 |
[RTextView setSelectedRange:targetRange];
|
|
|
778 |
[RTextView scrollRangeToVisible:targetRange];
|
|
|
779 |
}
|
| 568 |
iacus |
780 |
}
|
|
|
781 |
}
|
|
|
782 |
|
|
|
783 |
- (void) handleProcessingInput: (char*) cmd
|
|
|
784 |
{
|
| 788 |
urbaneks |
785 |
NSString *s = [[NSString alloc] initWithUTF8String:cmd];
|
| 568 |
iacus |
786 |
|
| 871 |
urbaneks |
787 |
@synchronized(textViewSync) {
|
|
|
788 |
unsigned textLength = [[RTextView textStorage] length];
|
|
|
789 |
|
|
|
790 |
[RTextView setSelectedRange:NSMakeRange(committedLength, textLength-committedLength)];
|
|
|
791 |
[RTextView insertText:s];
|
|
|
792 |
textLength = [[RTextView textStorage] length];
|
|
|
793 |
[RTextView setTextColor:[consoleColors objectAtIndex:iInputColor] range:NSMakeRange(committedLength, textLength-committedLength)];
|
|
|
794 |
outputPosition=committedLength=textLength;
|
| 901 |
urbaneks |
795 |
|
|
|
796 |
// remove undo actions to prevent undo across prompts
|
|
|
797 |
[[RTextView undoManager] removeAllActions];
|
| 871 |
urbaneks |
798 |
}
|
|
|
799 |
|
| 788 |
urbaneks |
800 |
[s release];
|
| 568 |
iacus |
801 |
|
| 1146 |
urbaneks |
802 |
// the ?/help hack is no longer needed in 2.1
|
|
|
803 |
#if (R_VERSION < R_Version(2,1,0))
|
| 568 |
iacus |
804 |
if((*cmd == '?') || (!strncmp("help(",cmd,5))){
|
|
|
805 |
[self openHelpFor: cmd];
|
|
|
806 |
cmd[0] = '\n'; cmd[1] = 0;
|
|
|
807 |
}
|
| 1146 |
urbaneks |
808 |
#endif
|
| 568 |
iacus |
809 |
}
|
|
|
810 |
|
|
|
811 |
- (char*) handleReadConsole: (int) addtohist
|
|
|
812 |
{
|
|
|
813 |
if (currentConsoleInput) {
|
|
|
814 |
[currentConsoleInput release];
|
|
|
815 |
currentConsoleInput=nil;
|
|
|
816 |
}
|
|
|
817 |
|
|
|
818 |
while ([consoleInputQueue count]==0)
|
|
|
819 |
[self doProcessEvents: YES];
|
|
|
820 |
|
|
|
821 |
currentConsoleInput = [consoleInputQueue objectAtIndex:0];
|
|
|
822 |
[consoleInputQueue removeObjectAtIndex:0];
|
| 783 |
urbaneks |
823 |
|
| 568 |
iacus |
824 |
if (addtohist) {
|
| 1257 |
urbaneks |
825 |
// Figure out how to get hold of ParseStatus here!
|
|
|
826 |
[hist commit:currentConsoleInput];
|
| 568 |
iacus |
827 |
[historyView reloadData];
|
|
|
828 |
}
|
|
|
829 |
|
| 755 |
urbaneks |
830 |
{
|
|
|
831 |
const char *c = [currentConsoleInput UTF8String];
|
|
|
832 |
if (!c) return 0;
|
|
|
833 |
if (strlen(c)>readConsTransBufferSize-1) { // grow as necessary
|
|
|
834 |
free(readConsTransBuffer);
|
|
|
835 |
readConsTransBufferSize = (strlen(c)+2048)&0xfffffc00;
|
|
|
836 |
readConsTransBuffer = (char*) malloc(readConsTransBufferSize);
|
|
|
837 |
} // we don't shrink the buffer if gets too large - we may want to think about that ...
|
|
|
838 |
|
|
|
839 |
strcpy(readConsTransBuffer, c);
|
|
|
840 |
}
|
|
|
841 |
return readConsTransBuffer;
|
| 568 |
iacus |
842 |
}
|
|
|
843 |
|
| 688 |
urbaneks |
844 |
- (int) handleEdit: (char*) file
|
|
|
845 |
{
|
| 824 |
urbaneks |
846 |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
847 |
NSString *fn = [[NSString stringWithUTF8String:file] stringByExpandingTildeInPath];
|
| 1420 |
urbaneks |
848 |
SLog(@"RController.handleEdit: %s", file);
|
|
|
849 |
|
| 824 |
urbaneks |
850 |
if (![[NSFileManager defaultManager] isReadableFileAtPath:fn]) {
|
|
|
851 |
[pool release];
|
|
|
852 |
return 0;
|
|
|
853 |
}
|
|
|
854 |
RDocument *document = [[RDocumentController sharedDocumentController] openRDocumentWithContentsOfFile: fn display:YES];
|
| 688 |
urbaneks |
855 |
[document setREditFlag: YES];
|
|
|
856 |
|
|
|
857 |
NSEnumerator *e = [[document windowControllers] objectEnumerator];
|
|
|
858 |
NSWindowController *wc = nil;
|
|
|
859 |
while (wc = [e nextObject]) {
|
|
|
860 |
NSWindow *window = [wc window];
|
|
|
861 |
NSModalSession session = [NSApp beginModalSessionForWindow:window];
|
| 1136 |
urbaneks |
862 |
while([document hasREditFlag]) {
|
| 688 |
urbaneks |
863 |
[NSApp runModalSession:session];
|
| 1136 |
urbaneks |
864 |
}
|
| 688 |
urbaneks |
865 |
[NSApp endModalSession:session];
|
|
|
866 |
}
|
|
|
867 |
|
| 824 |
urbaneks |
868 |
[pool release];
|
| 688 |
urbaneks |
869 |
return(0);
|
|
|
870 |
}
|
|
|
871 |
|
|
|
872 |
/* FIXME: the filename is not set for newvly created files */
|
|
|
873 |
- (int) handleEditFiles: (int) nfile withNames: (char**) file titles: (char**) wtitle pager: (char*) pager
|
|
|
874 |
{
|
|
|
875 |
int i;
|
|
|
876 |
|
| 1420 |
urbaneks |
877 |
SLog(@"RController.handleEditFiles (%d of them, pager %s)", nfile, pager);
|
| 688 |
urbaneks |
878 |
if (nfile <=0) return 1;
|
|
|
879 |
|
| 929 |
urbaneks |
880 |
for (i = 0; i < nfile; i++) {
|
|
|
881 |
NSString *fn = [[NSString stringWithUTF8String:file[i]] stringByExpandingTildeInPath];
|
|
|
882 |
if([[NSFileManager defaultManager] fileExistsAtPath:fn])
|
|
|
883 |
[[RDocumentController sharedDocumentController] openRDocumentWithContentsOfFile:fn display:true];
|
| 688 |
urbaneks |
884 |
else
|
| 1490 |
urbaneks |
885 |
[[NSDocumentController sharedDocumentController] newDocument: [RController sharedController]];
|
| 688 |
urbaneks |
886 |
|
|
|
887 |
NSDocument *document = [[NSDocumentController sharedDocumentController] currentDocument];
|
|
|
888 |
if(wtitle[i]!=nil)
|
| 929 |
urbaneks |
889 |
[RDocument changeDocumentTitle: document Title: [NSString stringWithUTF8String:wtitle[i]]];
|
| 688 |
urbaneks |
890 |
}
|
|
|
891 |
return 1;
|
|
|
892 |
}
|
|
|
893 |
|
|
|
894 |
- (int) handleShowFiles: (int) nfile withNames: (char**) file headers: (char**) headers windowTitle: (char*) wtitle pager: (char*) pages andDelete: (BOOL) del
|
|
|
895 |
{
|
|
|
896 |
int i;
|
|
|
897 |
|
|
|
898 |
if (nfile <=0) return 1;
|
| 1420 |
urbaneks |
899 |
SLog(@"RController.handleShowFiles (%d of them, title %s, pager %s)", nfile, wtitle, pages);
|
| 688 |
urbaneks |
900 |
|
|
|
901 |
for (i = 0; i < nfile; i++){
|
| 929 |
urbaneks |
902 |
NSString *fn = [[NSString stringWithUTF8String:file[i]] stringByExpandingTildeInPath];
|
| 1432 |
urbaneks |
903 |
RDocument *document = [[RDocumentController sharedDocumentController] openRDocumentWithContentsOfFile:fn display:NO];
|
|
|
904 |
// don't dsiplay - we need to prevent the window controller from using highlighting
|
| 1420 |
urbaneks |
905 |
if (document) {
|
| 1432 |
urbaneks |
906 |
NSArray *wcs = [document windowControllers];
|
|
|
907 |
if (wcs && [wcs count]>0) {
|
|
|
908 |
SLog(@" - Disabling syntax highlighting for this document");
|
|
|
909 |
[(RDocumentWinCtrl*) [wcs objectAtIndex:0] setPlain:YES];
|
|
|
910 |
}
|
| 1420 |
urbaneks |
911 |
if(wtitle[i]!=nil)
|
|
|
912 |
[RDocument changeDocumentTitle: document Title: [NSString stringWithUTF8String:wtitle]];
|
|
|
913 |
[document setEditable: NO];
|
| 1432 |
urbaneks |
914 |
SLog(@" - finally show the document window");
|
|
|
915 |
[document showWindows];
|
| 1420 |
urbaneks |
916 |
}
|
| 688 |
urbaneks |
917 |
}
|
|
|
918 |
return 1;
|
|
|
919 |
}
|
|
|
920 |
|
|
|
921 |
//======== Cocoa Handler ======
|
|
|
922 |
|
|
|
923 |
- (int) handlePackages: (int) count withNames: (char**) name descriptions: (char**) desc URLs: (char**) url status: (BOOL*) stat
|
|
|
924 |
{
|
|
|
925 |
[[PackageManager sharedController] updatePackages:count withNames:name descriptions:desc URLs:url status:stat];
|
|
|
926 |
return 0;
|
|
|
927 |
}
|
|
|
928 |
|
| 1252 |
urbaneks |
929 |
- (int) handleListItems: (int) count withNames: (char**) name status: (BOOL*) stat multiple: (BOOL) multiple title: (NSString*) title;
|
| 1212 |
iacus |
930 |
{
|
| 1255 |
urbaneks |
931 |
return [[SelectList sharedController] selectList:count withNames:name status:stat multiple: multiple title:title];
|
| 1212 |
iacus |
932 |
}
|
|
|
933 |
|
| 796 |
urbaneks |
934 |
- (int) handleHelpSearch: (int) count withTopics: (char**) topics packages: (char**) pkgs descriptions: (char**) descs urls: (char**) urls title: (char*) title
|
|
|
935 |
{
|
|
|
936 |
[[SearchTable sharedController] updateHelpSearch:count withTopics:topics packages:pkgs descriptions:descs urls:urls title:title];
|
|
|
937 |
return 0;
|
|
|
938 |
}
|
|
|
939 |
|
| 688 |
urbaneks |
940 |
- (BOOL*) handleDatasets: (int) count withNames: (char**) name descriptions: (char**) desc packages: (char**) pkg URLs: (char**) url
|
|
|
941 |
{
|
|
|
942 |
[[DataManager sharedController] updateDatasets:count withNames:name descriptions:desc packages:pkg URLs:url];
|
|
|
943 |
return 0; // we don't load the DS this way, we use REngine instead
|
|
|
944 |
}
|
|
|
945 |
|
|
|
946 |
- (int) handleInstalledPackages: (int) count withNames: (char**) name installedVersions: (char**) iver repositoryVersions: (char**) rver update: (BOOL*) stat label: (char*) label
|
|
|
947 |
{
|
|
|
948 |
[[PackageInstaller sharedController] updateInstalledPackages:count withNames:name installedVersions:iver repositoryVersions:rver update:stat label:label];
|
|
|
949 |
return 0;
|
|
|
950 |
}
|
|
|
951 |
|
|
|
952 |
- (int) handleSystemCommand: (char*) cmd
|
|
|
953 |
{
|
|
|
954 |
int cstat=-1;
|
|
|
955 |
pid_t pid;
|
|
|
956 |
|
|
|
957 |
if ([self getRootFlag]) {
|
|
|
958 |
FILE *f;
|
|
|
959 |
char *argv[3] = { "-c", cmd, 0 };
|
| 1328 |
urbaneks |
960 |
int res;
|
| 688 |
urbaneks |
961 |
NSBundle *b = [NSBundle mainBundle];
|
|
|
962 |
char *sushPath=0;
|
|
|
963 |
if (b) {
|
|
|
964 |
NSString *sush=[[b resourcePath] stringByAppendingString:@"/sush"];
|
|
|
965 |
sushPath = (char*) malloc([sush cStringLength]+1);
|
|
|
966 |
[sush getCString:sushPath maxLength:[sush cStringLength]];
|
|
|
967 |
}
|
|
|
968 |
|
| 1328 |
urbaneks |
969 |
res = runRootScript(sushPath?sushPath:"/bin/sh",argv,&f,1);
|
|
|
970 |
if (!res && f) {
|
|
|
971 |
int fd = fileno(f);
|
|
|
972 |
if (fd != -1) {
|
|
|
973 |
struct timespec peSleep = { 0, 50000000 }; // 50ms sleep
|
|
|
974 |
[self setRootFD:fileno(f)];
|
|
|
975 |
|
|
|
976 |
while (rootFD!=-1) { // readThread will reset rootFD to -1 when reaching EOF
|
|
|
977 |
nanosleep(&peSleep, 0); // sleep at least 50ms between PE calls (they're expensive)
|
|
|
978 |
Re_ProcessEvents();
|
|
|
979 |
}
|
|
|
980 |
}
|
|
|
981 |
}
|
| 688 |
urbaneks |
982 |
if (sushPath) free(sushPath);
|
| 1328 |
urbaneks |
983 |
return res;
|
| 688 |
urbaneks |
984 |
}
|
|
|
985 |
|
|
|
986 |
pid=fork();
|
|
|
987 |
if (pid==0) {
|
|
|
988 |
// int sr;
|
|
|
989 |
// reset signal handlers
|
|
|
990 |
signal(SIGINT, SIG_DFL);
|
|
|
991 |
signal(SIGTERM, SIG_DFL);
|
|
|
992 |
signal(SIGQUIT, SIG_DFL);
|
|
|
993 |
signal(SIGALRM, SIG_DFL);
|
|
|
994 |
signal(SIGCHLD, SIG_DFL);
|
| 1252 |
urbaneks |
995 |
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
|
| 688 |
urbaneks |
996 |
exit(-1);
|
|
|
997 |
//sr=system(cmd);
|
|
|
998 |
//exit(WEXITSTATUS(sr));
|
|
|
999 |
}
|
|
|
1000 |
if (pid==-1) return -1;
|
| 1472 |
urbaneks |
1001 |
|
|
|
1002 |
{
|
| 1477 |
urbaneks |
1003 |
struct timespec peSleep = { 0, 100000000 }; // 100ms sleep
|
| 1472 |
urbaneks |
1004 |
while (1) {
|
|
|
1005 |
pid_t w = waitpid(pid, &cstat, WNOHANG);
|
|
|
1006 |
if (w!=0) break;
|
|
|
1007 |
nanosleep(&peSleep, 0); // sleep at least 50ms between PE calls (they're expensive)
|
|
|
1008 |
Re_ProcessEvents();
|
|
|
1009 |
}
|
| 688 |
urbaneks |
1010 |
}
|
| 1490 |
urbaneks |
1011 |
[[RController sharedController] rmChildProcess: pid];
|
| 688 |
urbaneks |
1012 |
return cstat;
|
|
|
1013 |
}
|
|
|
1014 |
|
| 1146 |
urbaneks |
1015 |
- (int) handleCustomPrint: (char*) type withObject: (RSEXP*) obj
|
|
|
1016 |
{
|
| 1155 |
urbaneks |
1017 |
if (!obj) return -2;
|
| 1146 |
urbaneks |
1018 |
if (!strcmp(type, "help-files")) {
|
| 1155 |
urbaneks |
1019 |
RSEXP *x = [obj attr:@"topic"];
|
|
|
1020 |
NSString *topic = @"<unknown>";
|
|
|
1021 |
if (x && [x string]) topic = [x string];
|
|
|
1022 |
[x release];
|
|
|
1023 |
if ([obj type]==STRSXP && [obj length]>0) {
|
|
|
1024 |
[[HelpManager sharedController] showHelpUsingFile: [obj string] topic: topic];
|
| 1490 |
urbaneks |
1025 |
[[self window] makeKeyWindow];
|
|
|
1026 |
[[[HelpManager sharedController] window] makeKeyAndOrderFront:self];
|
| 1155 |
urbaneks |
1027 |
} else
|
|
|
1028 |
NSBeginAlertSheet(NLS(@"Help topic not found"),NLS(@"OK"),nil,nil,[RTextView window],self,nil,NULL,NULL,[NSString stringWithFormat: NLS(@"Help for the topic \"%@\" was not found."), topic]);
|
| 1146 |
urbaneks |
1029 |
}
|
|
|
1030 |
|
|
|
1031 |
return 0;
|
|
|
1032 |
}
|
|
|
1033 |
|
| 688 |
urbaneks |
1034 |
//==========
|
|
|
1035 |
|
| 568 |
iacus |
1036 |
- (BOOL)windowShouldClose:(id)sender
|
|
|
1037 |
{
|
| 1038 |
urbaneks |
1038 |
[[RDocumentController sharedDocumentController] closeAllDocumentsWithDelegate:self didCloseAllSelector:@selector(didCloseAll:) contextInfo:nil];
|
|
|
1039 |
return NO;
|
|
|
1040 |
}
|
| 568 |
iacus |
1041 |
|
| 1038 |
urbaneks |
1042 |
- (void)didCloseAll:(id)sender {
|
| 1438 |
urbaneks |
1043 |
[Preferences commit];
|
| 1191 |
urbaneks |
1044 |
[self doSaveHistory:nil];
|
| 917 |
urbaneks |
1045 |
NSBeginAlertSheet(NLS(@"Closing R session"),NLS(@"Save"),NLS(@"Don't Save"),NLS(@"Cancel"),[RTextView window],self,@selector(shouldCloseDidEnd:returnCode:contextInfo:),NULL,NULL,NLS(@"Save workspace image?"));
|
| 568 |
iacus |
1046 |
}
|
|
|
1047 |
|
|
|
1048 |
/* this gets called by the "wanna save?" sheet on window close */
|
|
|
1049 |
- (void) shouldCloseDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
| 1438 |
urbaneks |
1050 |
[Preferences commit];
|
| 568 |
iacus |
1051 |
if (returnCode==NSAlertDefaultReturn)
|
|
|
1052 |
[[REngine mainEngine] executeString:@"quit(\"yes\")"];
|
|
|
1053 |
if (returnCode==NSAlertAlternateReturn)
|
|
|
1054 |
[[REngine mainEngine] executeString:@"quit(\"no\")"];
|
|
|
1055 |
}
|
|
|
1056 |
|
|
|
1057 |
/* This is used to send commands through the GUI, i.e. from menus
|
| 783 |
urbaneks |
1058 |
The input replaces what the user is currently typing.
|
| 568 |
iacus |
1059 |
*/
|
|
|
1060 |
- (void) sendInput: (NSString*) text {
|
|
|
1061 |
[self consoleInput:text interactive:YES];
|
|
|
1062 |
/*
|
| 783 |
urbaneks |
1063 |
unsigned textLength = [[RTextView textStorage] length];
|
|
|
1064 |
[RTextView setSelectedRange:NSMakeRange(textLength, 0)];
|
|
|
1065 |
NSEvent* event = [NSEvent keyEventWithType:NSKeyDown
|
|
|
1066 |
location:NSMakePoint(0,0)
|
|
|
1067 |
modifierFlags:0
|
|
|
1068 |
timestamp:0
|
|
|
1069 |
windowNumber:[RConsoleWindow windowNumber]
|
|
|
1070 |
context:nil
|
|
|
1071 |
characters:@"\n"
|
|
|
1072 |
charactersIgnoringModifiers:nil
|
|
|
1073 |
isARepeat:NO
|
|
|
1074 |
keyCode:nil
|
|
|
1075 |
];
|
|
|
1076 |
[NSApp postEvent:event atStart:YES];
|
| 568 |
iacus |
1077 |
*/
|
|
|
1078 |
}
|
|
|
1079 |
|
|
|
1080 |
/* These two routines are needed to update the History TableView */
|
|
|
1081 |
- (int)numberOfRowsInTableView: (NSTableView *)tableView
|
|
|
1082 |
{
|
|
|
1083 |
return [[hist entries] count];
|
|
|
1084 |
}
|
|
|
1085 |
|
|
|
1086 |
- (id)tableView: (NSTableView *)tableView
|
|
|
1087 |
objectValueForTableColumn: (NSTableColumn *)tableColumn
|
| 783 |
urbaneks |
1088 |
row: (int)row
|
| 568 |
iacus |
1089 |
{
|
| 783 |
urbaneks |
1090 |
return (NSString*) [[hist entries] objectAtIndex: row];
|
| 568 |
iacus |
1091 |
}
|
|
|
1092 |
|
|
|
1093 |
/* Clears the history and updates the TableView */
|
|
|
1094 |
|
|
|
1095 |
- (IBAction)doClearHistory:(id)sender
|
|
|
1096 |
{
|
| 1457 |
urbaneks |
1097 |
SLog(@"RController.doClearHistory");
|
| 568 |
iacus |
1098 |
[hist resetAll];
|
|
|
1099 |
[historyView reloadData];
|
|
|
1100 |
}
|
|
|
1101 |
|
|
|
1102 |
- (IBAction)doLoadHistory:(id)sender
|
|
|
1103 |
{
|
| 1252 |
urbaneks |
1104 |
NSString *fname=nil;
|
| 1457 |
urbaneks |
1105 |
SLog(@"RController.doLoadHistory");
|
| 1191 |
urbaneks |
1106 |
if (sender) {
|
|
|
1107 |
NSOpenPanel *op = [NSOpenPanel openPanel];
|
|
|
1108 |
[op setDirectory:[[[NSFileManager defaultManager] currentDirectoryPath] stringByExpandingTildeInPath]];
|
|
|
1109 |
[op setTitle:NLS(@"Choose history File")];
|
|
|
1110 |
if([op runModalForTypes: [NSArray arrayWithObject:@"history"]] == NSOKButton)
|
|
|
1111 |
fname = [op filename];
|
|
|
1112 |
} else
|
|
|
1113 |
fname = [[Preferences stringForKey:historyFileNamePathKey
|
|
|
1114 |
withDefault: @".Rhistory"] stringByExpandingTildeInPath];
|
| 1457 |
urbaneks |
1115 |
SLog(@" - history file to load: %@", fname);
|
| 1191 |
urbaneks |
1116 |
if(fname != nil){
|
| 1457 |
urbaneks |
1117 |
fname = [[[[NSFileManager defaultManager] currentDirectoryPath] stringByExpandingTildeInPath] stringByAppendingString:[NSString stringWithFormat:@"/%@", fname]];
|
| 1257 |
urbaneks |
1118 |
[self doClearHistory:nil];
|
| 1457 |
urbaneks |
1119 |
|
|
|
1120 |
SLog(@" - cleared history, reload with: %@", fname);
|
| 1191 |
urbaneks |
1121 |
if ([[NSFileManager defaultManager] fileExistsAtPath: fname]) {
|
| 1457 |
urbaneks |
1122 |
FILE *rhist = fopen([fname UTF8String], "r");
|
|
|
1123 |
if (!rhist) {
|
| 1191 |
urbaneks |
1124 |
NSLog(NLS(@"Can't open history file %2"), fname);
|
|
|
1125 |
} else {
|
| 1457 |
urbaneks |
1126 |
NSString *entry = nil;
|
|
|
1127 |
char c[1024];
|
|
|
1128 |
|
|
|
1129 |
c[1023]=0;
|
|
|
1130 |
while(fgets(c, 1023, rhist) && *c) {
|
|
|
1131 |
int i = strlen(c);
|
|
|
1132 |
BOOL multiline = NO;
|
|
|
1133 |
|
|
|
1134 |
while (i>0 && (c[i-1]=='\n' || c[i-1]=='\r')) c[--i]=0; // just in case someone has PC history we strip \r too
|
|
|
1135 |
if (!*c) continue; // skip blank lines (is that intended? what about "foo#\n\nbla\n"?)
|
|
|
1136 |
if (multiline=(c[i-1]=='#')) c[i-1]='\n';
|
|
|
1137 |
if (entry)
|
|
|
1138 |
entry = [entry stringByAppendingString:[NSString stringWithUTF8String:c]];
|
|
|
1139 |
else
|
|
|
1140 |
entry = [NSString stringWithUTF8String:c];
|
|
|
1141 |
if (!multiline) {
|
|
|
1142 |
[hist commit:entry];
|
|
|
1143 |
entry=nil;
|
| 1191 |
urbaneks |
1144 |
}
|
|
|
1145 |
}
|
| 1457 |
urbaneks |
1146 |
if (entry) [hist commit:entry]; // just being paranoid if someone edited the file manually
|
|
|
1147 |
fclose(rhist);
|
| 1191 |
urbaneks |
1148 |
}
|
|
|
1149 |
}
|
|
|
1150 |
[historyView scrollRowToVisible:[historyView numberOfRows]];
|
|
|
1151 |
[historyView reloadData];
|
| 568 |
iacus |
1152 |
}
|
|
|
1153 |
}
|
|
|
1154 |
|
| 1136 |
urbaneks |
1155 |
- (void)doSaveHistory:(id)sender {
|
| 1252 |
urbaneks |
1156 |
NSString *fname = nil;
|
| 1457 |
urbaneks |
1157 |
FILE *rhist;
|
|
|
1158 |
|
| 1191 |
urbaneks |
1159 |
if (sender) {
|
|
|
1160 |
NSSavePanel *sp = [NSSavePanel savePanel];
|
|
|
1161 |
[sp setDirectory:[[[NSFileManager defaultManager] currentDirectoryPath] stringByExpandingTildeInPath]];
|
|
|
1162 |
[sp setRequiredFileType:@"history"];
|
|
|
1163 |
[sp setTitle:NLS(@"Save history File")];
|
|
|
1164 |
if([sp runModal] == NSOKButton) fname = [sp filename];
|
|
|
1165 |
} else
|
|
|
1166 |
fname = [[Preferences stringForKey:historyFileNamePathKey
|
|
|
1167 |
withDefault: @".Rhistory"] stringByExpandingTildeInPath];
|
| 1457 |
urbaneks |
1168 |
|
|
|
1169 |
SLog(@"RController.doSaveHistory (file %@)", fname);
|
|
|
1170 |
rhist = fopen([fname UTF8String], "w");
|
|
|
1171 |
if (!rhist) {
|
| 1191 |
urbaneks |
1172 |
NSLog(NLS(@"Can't open history file %2"), fname);
|
|
|
1173 |
} else {
|
| 1457 |
urbaneks |
1174 |
NSEnumerator *enumerator = [[hist entries] objectEnumerator];
|
|
|
1175 |
NSString *entry;
|
|
|
1176 |
|
|
|
1177 |
while (entry = [enumerator nextObject]) {
|
|
|
1178 |
if ([entry rangeOfString:@"\n" options:NSLiteralSearch].location!=NSNotFound) { // add # before \n for multi-line strings
|
|
|
1179 |
entry = [entry mutableCopy];
|
|
|
1180 |
[(NSMutableString*)entry replaceOccurrencesOfString:@"\n" withString:@"#\n" options:NSLiteralSearch range:NSMakeRange(0,[entry length])];
|
| 1191 |
urbaneks |
1181 |
}
|
| 1457 |
urbaneks |
1182 |
fputs([entry UTF8String], rhist); // not 100% safe
|
|
|
1183 |
fputc('\n', rhist); // trailing \n
|
| 1191 |
urbaneks |
1184 |
}
|
| 1457 |
urbaneks |
1185 |
|
| 1191 |
urbaneks |
1186 |
fclose(rhist);
|
| 568 |
iacus |
1187 |
}
|
|
|
1188 |
}
|
|
|
1189 |
|
|
|
1190 |
/* On double-click on items of the History TableView, the item is pasted into the console
|
| 783 |
urbaneks |
1191 |
at current cursor position
|
| 568 |
iacus |
1192 |
*/
|
|
|
1193 |
- (IBAction)historyDoubleClick:(id)sender {
|
|
|
1194 |
NSString *cmd;
|
| 1257 |
urbaneks |
1195 |
int index = [historyView selectedRow];
|
| 568 |
iacus |
1196 |
if(index == -1) return;
|
|
|
1197 |
|
|
|
1198 |
cmd = [[hist entries] objectAtIndex:index];
|
|
|
1199 |
[self consoleInput:cmd interactive:NO];
|
|
|
1200 |
[RConsoleWindow makeFirstResponder:RTextView];
|
|
|
1201 |
}
|
|
|
1202 |
|
| 1257 |
urbaneks |
1203 |
- (IBAction)historyDeleteEntry:(id)sender {
|
|
|
1204 |
int index = [historyView selectedRow];
|
|
|
1205 |
if((index >= 0) && (index < [[hist entries] count]))
|
|
|
1206 |
[hist deleteEntry:index];
|
|
|
1207 |
[historyView reloadData];
|
|
|
1208 |
}
|
|
|
1209 |
|
| 568 |
iacus |
1210 |
/* This routine is intended to "cat" some text to the R Console without
|
| 783 |
urbaneks |
1211 |
issuing the newline.
|
| 568 |
iacus |
1212 |
- (void) consolePaste: (NSString*) text {
|
|
|
1213 |
unsigned textLength = [[RTextView textStorage] length];
|
|
|
1214 |
[RTextView setSelectedRange:NSMakeRange(textLength, 0)];
|
|
|
1215 |
[RTextView insertText:text];
|
|
|
1216 |
}
|
|
|
1217 |
*/
|
|
|
1218 |
/* This function is used by two threads to write stderr and/or stdout to the console
|
| 783 |
urbaneks |
1219 |
outputType: 0 = stdout, 1 = stderr, 2 = stdout/err as root
|
| 568 |
iacus |
1220 |
*/
|
|
|
1221 |
- (void) writeLogsWithBytes: (char*) buf length: (int) len type: (int) outputType
|
|
|
1222 |
{
|
| 815 |
urbaneks |
1223 |
NSColor *color=(outputType==0)?[consoleColors objectAtIndex:iStdoutColor]:((outputType==1)?[consoleColors objectAtIndex:iStderrColor]:[consoleColors objectAtIndex:iRootColor]);
|
| 788 |
urbaneks |
1224 |
buf[len]=0; /* this MAY be dangerous ... */
|
|
|
1225 |
NSString *s = [[NSString alloc] initWithUTF8String:buf];
|
| 575 |
urbaneks |
1226 |
[self flushROutput];
|
| 788 |
urbaneks |
1227 |
[self writeConsoleDirectly:s withColor:color];
|
|
|
1228 |
[s release];
|
|
|
1229 |
}
|
| 568 |
iacus |
1230 |
|
| 1490 |
urbaneks |
1231 |
+ (RController*) sharedController{
|
| 568 |
iacus |
1232 |
return sharedRController;
|
|
|
1233 |
}
|
|
|
1234 |
|
|
|
1235 |
/* console input - the string passed here is handled as if it was typed on the console */
|
|
|
1236 |
- (void) consoleInput: (NSString*) cmd interactive: (BOOL) inter
|
|
|
1237 |
{
|
| 871 |
urbaneks |
1238 |
@synchronized(textViewSync) {
|
|
|
1239 |
if (!inter) {
|
|
|
1240 |
int textLength = [[RTextView textStorage] length];
|
|
|
1241 |
if (textLength>committedLength)
|
|
|
1242 |
[RTextView replaceCharactersInRange:NSMakeRange(committedLength,textLength-committedLength) withString:@""];
|
|
|
1243 |
[RTextView setSelectedRange:NSMakeRange(committedLength,0)];
|
|
|
1244 |
[RTextView insertText: cmd];
|
|
|
1245 |
textLength = [[RTextView textStorage] length];
|
|
|
1246 |
[RTextView setTextColor:[consoleColors objectAtIndex:iInputColor] range:NSMakeRange(committedLength,textLength-committedLength)];
|
|
|
1247 |
}
|
|
|
1248 |
|
|
|
1249 |
if (inter) {
|
|
|
1250 |
if ([cmd characterAtIndex:[cmd length]-1]!='\n') cmd=[cmd stringByAppendingString: @"\n"];
|
|
|
1251 |
[consoleInputQueue addObject:[[NSString alloc] initWithString:cmd]];
|
| 1488 |
urbaneks |
1252 |
[self setStatusLineText:@""];
|
| 871 |
urbaneks |
1253 |
}
|
| 568 |
iacus |
1254 |
}
|
|
|
1255 |
}
|
|
|
1256 |
|
|
|
1257 |
- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
|
|
|
1258 |
BOOL retval = NO;
|
| 783 |
urbaneks |
1259 |
|
| 568 |
iacus |
1260 |
//NSLog(@"RTextView commandSelector: %@\n", NSStringFromSelector(commandSelector));
|
|
|
1261 |
|
|
|
1262 |
if (@selector(insertNewline:) == commandSelector) {
|
|
|
1263 |
unsigned textLength = [[textView textStorage] length];
|
|
|
1264 |
[textView setSelectedRange:NSMakeRange(textLength,0)];
|
|
|
1265 |
if (textLength >= committedLength) {
|
|
|
1266 |
[textView insertText:@"\n"];
|
|
|
1267 |
textLength = [[textView textStorage] length];
|
|
|
1268 |
[self consoleInput: [[textView attributedSubstringFromRange:NSMakeRange(committedLength, textLength - committedLength)] string] interactive: YES];
|
|
|
1269 |
return(YES);
|
|
|
1270 |
}
|
|
|
1271 |
retval = YES;
|
|
|
1272 |
}
|
| 1257 |
urbaneks |
1273 |
|
| 568 |
iacus |
1274 |
// ---- history browsing ----
|
|
|
1275 |
if (@selector(moveUp:) == commandSelector) {
|
|
|
1276 |
unsigned textLength = [[textView textStorage] length];
|
|
|
1277 |
NSRange sr=[textView selectedRange];
|
|
|
1278 |
if (sr.location==committedLength || sr.location==textLength) {
|
|
|
1279 |
NSRange rr=NSMakeRange(committedLength, textLength-committedLength);
|
|
|
1280 |
NSString *text = [[textView attributedSubstringFromRange:rr] string];
|
|
|
1281 |
if ([hist isDirty]) {
|
|
|
1282 |
[hist updateDirty: text];
|
|
|
1283 |
}
|
|
|
1284 |
NSString *news = [hist prev];
|
|
|
1285 |
if (news!=nil) {
|
|
|
1286 |
[news retain];
|
|
|
1287 |
sr.length=0; sr.location=committedLength;
|
|
|
1288 |
[textView setSelectedRange:sr];
|
|
|
1289 |
[textView replaceCharactersInRange:rr withString:news];
|
|
|
1290 |
[textView insertText:@""];
|
|
|
1291 |
[news release];
|
|
|
1292 |
}
|
|
|
1293 |
retval = YES;
|
|
|
1294 |
}
|
|
|
1295 |
}
|
|
|
1296 |
if (@selector(moveDown:) == commandSelector) {
|
|
|
1297 |
unsigned textLength = [[textView textStorage] length];
|
|
|
1298 |
NSRange sr=[textView selectedRange];
|
|
|
1299 |
if ((sr.location==committedLength || sr.location==textLength) && ![hist isDirty]) {
|
|
|
1300 |
NSRange rr=NSMakeRange(committedLength, textLength-committedLength);
|
|
|
1301 |
NSString *news = [hist next];
|
|
|
1302 |
if (news==nil) news=@""; else [news retain];
|
|
|
1303 |
sr.length=0; sr.location=committedLength;
|
|
|
1304 |
[textView setSelectedRange:sr];
|
|
|
1305 |
[textView replaceCharactersInRange:rr withString:news];
|
|
|
1306 |
[textView insertText:@""];
|
|
|
1307 |
[news release];
|
|
|
1308 |
retval = YES;
|
|
|
1309 |
}
|
|
|
1310 |
}
|
|
|
1311 |
|
|
|
1312 |
// ---- make sure the user won't accidentally get out of the input line ----
|
|
|
1313 |
|
|
|
1314 |
if (@selector(moveToBeginningOfParagraph:) == commandSelector || @selector(moveToBeginningOfLine:) == commandSelector) {
|
|
|
1315 |
[textView setSelectedRange: NSMakeRange(committedLength,0)];
|
|
|
1316 |
retval = YES;
|
|
|
1317 |
}
|
| 783 |
urbaneks |
1318 |
|
| 568 |
iacus |
1319 |
if (@selector(moveToBeginningOfParagraphAndModifySelection:) == commandSelector || @selector(moveToBeginningOfLineAndModifySelection:) == commandSelector) {
|
|
|
1320 |
// FIXME: this kills the selection - we should retain it ...
|
|
|
1321 |
[textView setSelectedRange: NSMakeRange(committedLength,0)];
|
|
|
1322 |
retval = YES;
|
|
|
1323 |
}
|
|
|
1324 |
|
|
|
1325 |
if (@selector(moveWordLeft:) == commandSelector || @selector(moveLeft:) == commandSelector ||
|
|
|
1326 |
@selector(moveWordLeftAndModifySelection:) == commandSelector || @selector(moveLeftAndModifySelection:) == commandSelector) {
|
|
|
1327 |
NSRange sr=[textView selectedRange];
|
|
|
1328 |
if (sr.location==committedLength) return YES;
|
|
|
1329 |
}
|
|
|
1330 |
|
|
|
1331 |
// ---- code/file completion ----
|
|
|
1332 |
|
|
|
1333 |
if (@selector(insertTab:) == commandSelector) {
|
| 974 |
urbaneks |
1334 |
[textView complete:self];
|
|
|
1335 |
retval = YES;
|
| 568 |
iacus |
1336 |
}
|
|
|
1337 |
|
|
|
1338 |
// ---- cancel ---
|
|
|
1339 |
|
|
|
1340 |
if (@selector(cancel:) == commandSelector) {
|
|
|
1341 |
[self breakR:self];
|
|
|
1342 |
retval = YES;
|
|
|
1343 |
}
|
|
|
1344 |
|
|
|
1345 |
return retval;
|
|
|
1346 |
}
|
| 783 |
urbaneks |
1347 |
|
| 1488 |
urbaneks |
1348 |
- (void)textStorageDidProcessEditing:(NSNotification *)aNotification {
|
|
|
1349 |
NSTextStorage *ts = [aNotification object];
|
|
|
1350 |
NSString *s = [ts string];
|
|
|
1351 |
NSRange sr = [RTextView selectedRange];
|
|
|
1352 |
|
|
|
1353 |
// check for a typed (
|
| 1603 |
urbaneks |
1354 |
if (argsHints && sr.location>committedLength && sr.length==0 && sr.location>0 && sr.location<[s length] && [s characterAtIndex:sr.location]=='(') {
|
| 1488 |
urbaneks |
1355 |
int i = sr.location-1;
|
|
|
1356 |
unichar c = [s characterAtIndex:i];
|
|
|
1357 |
BOOL hasLit = NO;
|
|
|
1358 |
while ((c>='0' && c<='9') || (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='.' || c=='_') {
|
|
|
1359 |
if (!hasLit && ((c>='a' && c<='z') || (c>='A' && c<='Z'))) hasLit=YES;
|
|
|
1360 |
i--;
|
|
|
1361 |
if (i<0) break;
|
|
|
1362 |
c = [s characterAtIndex:i];
|
|
|
1363 |
}
|
|
|
1364 |
i++;
|
|
|
1365 |
if (hasLit && sr.location>i)
|
|
|
1366 |
[self hintForFunction: [s substringWithRange:NSMakeRange(i,sr.location-i)]];
|
|
|
1367 |
}
|
|
|
1368 |
}
|
|
|
1369 |
|
|
|
1370 |
- (BOOL) hintForFunction: (NSString*) fn
|
|
|
1371 |
{
|
|
|
1372 |
BOOL success = NO;
|
| 1499 |
urbaneks |
1373 |
if (insideR>0) {
|
|
|
1374 |
[self setStatusLineText:NLS(@"(arguments lookup is disabled while R is busy)")];
|
|
|
1375 |
return NO;
|
|
|
1376 |
}
|
| 1562 |
urbaneks |
1377 |
if (![[REngine mainEngine] beginProtected]) {
|
|
|
1378 |
[self setStatusLineText:NLS(@"(arguments lookup is disabled while R is busy)")];
|
|
|
1379 |
return NO;
|
|
|
1380 |
}
|
| 1488 |
urbaneks |
1381 |
RSEXP *x = [[REngine mainEngine] evaluateString:[NSString stringWithFormat:@"try(gsub('\\\\s+',' ',paste(capture.output(print(args(%@))),collapse='')),silent=TRUE)", fn]];
|
|
|
1382 |
if (x) {
|
|
|
1383 |
NSString *res = [x string];
|
|
|
1384 |
if (res && [res length]>10 && [res hasPrefix:@"function"]) {
|
|
|
1385 |
if ([res hasSuffix:@" NULL"]) res=[res substringToIndex:[res length]-5];
|
|
|
1386 |
res = [fn stringByAppendingString:[res substringFromIndex:9]];
|
|
|
1387 |
success = YES;
|
|
|
1388 |
[self setStatusLineText:res];
|
|
|
1389 |
}
|
|
|
1390 |
[x release];
|
|
|
1391 |
}
|
| 1562 |
urbaneks |
1392 |
[[REngine mainEngine] endProtected];
|
| 1488 |
urbaneks |
1393 |
return success;
|
|
|
1394 |
}
|
|
|
1395 |
|
| 568 |
iacus |
1396 |
/* Allow changes only for uncommitted text */
|
|
|
1397 |
- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString {
|
| 1436 |
urbaneks |
1398 |
if (replacementString && /* on font change we get nil replacementString which is ok to pass through */
|
|
|
1399 |
affectedCharRange.location < committedLength) { /* if the insertion is outside editable scope, append at the end */
|
| 623 |
urbaneks |
1400 |
[textView setSelectedRange:NSMakeRange([[textView textStorage] length],0)];
|
|
|
1401 |
[textView insertText:replacementString];
|
|
|
1402 |
return NO;
|
|
|
1403 |
}
|
|
|
1404 |
return YES;
|
| 568 |
iacus |
1405 |
}
|
| 783 |
urbaneks |
1406 |
|
| 974 |
urbaneks |
1407 |
- (NSArray *)textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index
|
|
|
1408 |
{
|
|
|
1409 |
NSRange sr=[textView selectedRange];
|
| 1499 |
urbaneks |
1410 |
SLog(@"completion attempt; cursor at %d, complRange: %d-%d, commit: %d", sr.location, charRange.location, charRange.location+charRange.length, committedLength);
|
| 974 |
urbaneks |
1411 |
//sr=charRange;
|
|
|
1412 |
int bow=sr.location;
|
|
|
1413 |
if (bow>committedLength) {
|
|
|
1414 |
while (bow>committedLength) bow--;
|
|
|
1415 |
{
|
|
|
1416 |
NSString *rep=nil;
|
|
|
1417 |
NSRange er = NSMakeRange(bow,sr.location-bow);
|
|
|
1418 |
NSString *text = [[textView attributedSubstringFromRange:er] string];
|
|
|
1419 |
|
|
|
1420 |
// first we need to find out whether we're in a text part or code part
|
|
|
1421 |
unichar c;
|
|
|
1422 |
int tl = [text length], tp=0, quotes=0, dquotes=0, lastQuote=-1;
|
|
|
1423 |
while (tp<tl) {
|
|
|
1424 |
c=[text characterAtIndex:tp];
|
|
|
1425 |
if (c=='\\') tp++; // skip the next char after a backslash (we don't have to worry about \023 and friends)
|
|
|
1426 |
else {
|
|
|
1427 |
if (dquotes==0 && c=='\'') {
|
|
|
1428 |
quotes^=1;
|
|
|
1429 |
if (quotes) lastQuote=tp;
|
|
|
1430 |
}
|
|
|
1431 |
if (quotes==0 && c=='"') {
|
|
|
1432 |
dquotes^=1;
|
|
|
1433 |
if (dquotes) lastQuote=tp;
|
|
|
1434 |
}
|
|
|
1435 |
}
|
|
|
1436 |
tp++;
|
|
|
1437 |
}
|
|
|
1438 |
|
|
|
1439 |
if (quotes+dquotes>0) { // if we're inside any quotes, use file completion
|
|
|
1440 |
//rep=[FileCompletion complete:[text substringFromIndex:lastQuote+1]];
|
|
|
1441 |
er.location+=lastQuote+1;
|
|
|
1442 |
er.length-=lastQuote+1;
|
|
|
1443 |
return [FileCompletion completeAll:[text substringFromIndex:lastQuote+1] cutPrefix:0];
|
|
|
1444 |
} else { // otherwise use code completion
|
|
|
1445 |
int s = [text length]-1;
|
|
|
1446 |
c = [text characterAtIndex:s];
|
|
|
1447 |
while (((c>='a')&&(c<='z'))||((c>='A')&&(c<='Z'))||((c>='0')&&(c<='9'))||c=='.') {
|
|
|
1448 |
s--;
|
|
|
1449 |
if (s==-1) break;
|
|
|
1450 |
c = [text characterAtIndex:s];
|
|
|
1451 |
}
|
|
|
1452 |
s++;
|
|
|
1453 |
er.location+=s; er.length-=s;
|
|
|
1454 |
//rep=[CodeCompletion complete:[text substringFromIndex:s]];
|
|
|
1455 |
*index=0;
|
| 1488 |
urbaneks |
1456 |
{
|
|
|
1457 |
NSArray *ca = [CodeCompletion completeAll:[text substringFromIndex:s] cutPrefix:charRange.location-er.location];
|
|
|
1458 |
if (ca && [ca count]==1) [self hintForFunction:[[ca objectAtIndex:0] substringToIndex:[[ca objectAtIndex:0] length]-1]];
|
|
|
1459 |
return ca;
|
|
|
1460 |
}
|
| 974 |
urbaneks |
1461 |
}
|
|
|
1462 |
|
|
|
1463 |
// ok, by now we should get "rep" if completion is possible and "er" modified to match the proper part
|
|
|
1464 |
if (rep!=nil) {
|
|
|
1465 |
*index=0;
|
|
|
1466 |
return [NSArray arrayWithObjects: rep, @"dummy", nil];
|
|
|
1467 |
//[textView replaceCharactersInRange:er withString:rep];
|
|
|
1468 |
}
|
|
|
1469 |
}
|
|
|
1470 |
}
|
|
|
1471 |
return nil;
|
|
|
1472 |
}
|
|
|
1473 |
|
| 568 |
iacus |
1474 |
- (void) handleBusy: (BOOL) isBusy {
|
|
|
1475 |
if (isBusy)
|
|
|
1476 |
[progressWheel startAnimation:self];
|
|
|
1477 |
else
|
|
|
1478 |
[progressWheel stopAnimation:self];
|
| 783 |
urbaneks |
1479 |
|
| 568 |
iacus |
1480 |
busyRFlag = isBusy;
|
|
|
1481 |
if (toolbarStopItem) {
|
|
|
1482 |
if (isBusy || childPID>0)
|
|
|
1483 |
[toolbarStopItem setEnabled:YES];
|
|
|
1484 |
else
|
|
|
1485 |
[toolbarStopItem setEnabled:NO];
|
|
|
1486 |
}
|
|
|
1487 |
}
|
|
|
1488 |
|
|
|
1489 |
- (void) handleShowMessage: (char*) msg
|
|
|
1490 |
{
|
| 1457 |
urbaneks |
1491 |
NSRunAlertPanel(NLS(@"R Message"),[NSString stringWithUTF8String:msg],NLS(@"OK"),nil,nil);
|
| 568 |
iacus |
1492 |
}
|
|
|
1493 |
|
|
|
1494 |
- (IBAction)flushconsole:(id)sender {
|
|
|
1495 |
[self handleFlushConsole];
|
|
|
1496 |
}
|
|
|
1497 |
|
|
|
1498 |
- (IBAction)otherEventLoops:(id)sender {
|
|
|
1499 |
R_runHandlers(R_InputHandlers, R_checkActivity(0, 1));
|
|
|
1500 |
}
|
|
|
1501 |
|
|
|
1502 |
|
|
|
1503 |
- (IBAction)newQuartzDevice:(id)sender {
|
| 849 |
urbaneks |
1504 |
NSString *width = [Preferences stringForKey:quartzPrefPaneWidthKey withDefault: @"4.5"];
|
|
|
1505 |
NSString *height = [Preferences stringForKey:quartzPrefPaneHeightKey withDefault: @"4.5"];
|
|
|
1506 |
NSString *cmd = [[[[@"quartz(display=\"\",width=" stringByAppendingString:width]
|
|
|
1507 |
stringByAppendingString:@",height="] stringByAppendingString:height] stringByAppendingString:@")"];
|
|
|
1508 |
[[REngine mainEngine] executeString:cmd];
|
| 568 |
iacus |
1509 |
}
|
|
|
1510 |
|
|
|
1511 |
- (IBAction)breakR:(id)sender{
|
|
|
1512 |
if (childPID)
|
|
|
1513 |
kill(childPID, SIGINT);
|
|
|
1514 |
else
|
|
|
1515 |
onintr();
|
|
|
1516 |
}
|
|
|
1517 |
|
|
|
1518 |
- (IBAction)quitR:(id)sender{
|
|
|
1519 |
[self windowShouldClose:RConsoleWindow];
|
|
|
1520 |
}
|
|
|
1521 |
|
| 577 |
urbaneks |
1522 |
- (IBAction)makeConsoleKey:(id)sender
|
|
|
1523 |
{
|
|
|
1524 |
[RConsoleWindow makeKeyAndOrderFront:sender];
|
|
|
1525 |
}
|
|
|
1526 |
|
| 568 |
iacus |
1527 |
- (IBAction)toggleHistory:(id)sender{
|
| 1136 |
urbaneks |
1528 |
NSDrawerState state = [HistoryDrawer state];
|
|
|
1529 |
if (NSDrawerOpeningState == state || NSDrawerOpenState == state) {
|
|
|
1530 |
[HistoryDrawer close];
|
|
|
1531 |
} else {
|
|
|
1532 |
[HistoryDrawer open];
|
|
|
1533 |
}
|
| 568 |
iacus |
1534 |
}
|
|
|
1535 |
|
|
|
1536 |
- (IBAction)toggleAuthentication:(id)sender{
|
|
|
1537 |
BOOL isOn = [self getRootFlag];
|
|
|
1538 |
|
|
|
1539 |
if (isOn) {
|
|
|
1540 |
removeRootAuthorization();
|
|
|
1541 |
[self setRootFlag:NO];
|
|
|
1542 |
} else {
|
|
|
1543 |
if (requestRootAuthorization(1)) return;
|
|
|
1544 |
[self setRootFlag:YES];
|
|
|
1545 |
}
|
|
|
1546 |
}
|
|
|
1547 |
|
| 577 |
urbaneks |
1548 |
- (IBAction)newDocument:(id)sender{
|
| 568 |
iacus |
1549 |
[[NSDocumentController sharedDocumentController] newDocument: sender];
|
|
|
1550 |
}
|
|
|
1551 |
|
| 905 |
urbaneks |
1552 |
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
|
| 1457 |
urbaneks |
1553 |
SLog(@" - application:openFile:%@ called", (NSString *)filename);
|
|
|
1554 |
NSString *dirname = @"";
|
| 1191 |
urbaneks |
1555 |
BOOL isDir;
|
|
|
1556 |
BOOL flag = [Preferences flagForKey:enforceInitialWorkingDirectoryKey withDefault:NO];
|
|
|
1557 |
NSFileManager *manager = [NSFileManager defaultManager];
|
|
|
1558 |
if ([manager fileExistsAtPath:filename isDirectory:&isDir] && isDir){
|
|
|
1559 |
if (!flag && !appLaunched) {
|
|
|
1560 |
[manager changeCurrentDirectoryPath:[filename stringByExpandingTildeInPath]];
|
|
|
1561 |
[self showWorkingDir:nil];
|
| 1457 |
urbaneks |
1562 |
[self doClearHistory:nil];
|
|
|
1563 |
[self doLoadHistory:nil];
|
| 1191 |
urbaneks |
1564 |
}
|
|
|
1565 |
} else {
|
|
|
1566 |
if (!flag && !appLaunched) {
|
| 1252 |
urbaneks |
1567 |
int i, j=-1;
|
| 1191 |
urbaneks |
1568 |
for (i=[filename length]-1 ; i>=0 ; i--) {
|
|
|
1569 |
if ([filename characterAtIndex:i] == '/') {
|
|
|
1570 |
j = i; i = -1;
|
|
|
1571 |
};
|
|
|
1572 |
}
|
| 1457 |
urbaneks |
1573 |
dirname = [filename substringWithRange: NSMakeRange(0, j+1)];
|
| 1191 |
urbaneks |
1574 |
[manager changeCurrentDirectoryPath:[dirname stringByExpandingTildeInPath]];
|
|
|
1575 |
[self showWorkingDir:nil];
|
|
|
1576 |
}
|
| 1457 |
urbaneks |
1577 |
[self doClearHistory:nil];
|
|
|
1578 |
[self doLoadHistory:nil];
|
| 1191 |
urbaneks |
1579 |
BOOL openInEditor = [Preferences flagForKey:editOrSourceKey withDefault: YES];
|
|
|
1580 |
if (openInEditor || appLaunched)
|
|
|
1581 |
[[RDocumentController sharedDocumentController] openDocumentWithContentsOfFile: filename display:YES];
|
|
|
1582 |
else
|
|
|
1583 |
[self sendInput:[NSString stringWithFormat:@"source(\"%@\")",[filename stringByExpandingTildeInPath]]];
|
|
|
1584 |
}
|
| 1457 |
urbaneks |
1585 |
SLog(@" - application:openFile:%@ with wd: <%@> done", (NSString *)filename, dirname);
|
| 905 |
urbaneks |
1586 |
return YES;
|
|
|
1587 |
}
|
|
|
1588 |
|
| 577 |
urbaneks |
1589 |
- (IBAction)openDocument:(id)sender{
|
| 568 |
iacus |
1590 |
[[NSDocumentController sharedDocumentController] openDocument: sender];
|
|
|
1591 |
}
|
|
|
1592 |
|
| 577 |
urbaneks |
1593 |
- (IBAction)saveDocumentAs:(id)sender{
|
|
|
1594 |
NSDocument *cd = [[NSDocumentController sharedDocumentController] currentDocument];
|
|
|
1595 |
|
|
|
1596 |
if (cd)
|
|
|
1597 |
[cd saveDocumentAs:sender];
|
|
|
1598 |
else {
|
|
|
1599 |
int answer;
|
|
|
1600 |
NSSavePanel *sp;
|
|
|
1601 |
sp = [NSSavePanel savePanel];
|
|
|
1602 |
[sp setRequiredFileType:@"txt"];
|
| 917 |
urbaneks |
1603 |
[sp setTitle:NLS(@"Save R Console To File")];
|
| 577 |
urbaneks |
1604 |
answer = [sp runModalForDirectory:nil file:@"R Console.txt"];
|
|
|
1605 |
|
|
|
1606 |
if(answer == NSOKButton) {
|
|
|
1607 |
[[RTextView string] writeToFile:[sp filename] atomically:YES];
|
|
|
1608 |
}
|
|
|
1609 |
}
|
| 568 |
iacus |
1610 |
}
|
|
|
1611 |
|
|
|
1612 |
- (IBAction)saveDocument:(id)sender{
|
| 577 |
urbaneks |
1613 |
NSDocument *cd = [[NSDocumentController sharedDocumentController] currentDocument];
|
| 568 |
iacus |
1614 |
|
| 577 |
urbaneks |
1615 |
if (cd)
|
|
|
1616 |
[cd saveDocument:sender];
|
|
|
1617 |
else // for the console this is the same as Save As ..
|
|
|
1618 |
[self saveDocumentAs:sender];
|
| 568 |
iacus |
1619 |
}
|
|
|
1620 |
|
|
|
1621 |
- (int) handleChooseFile:(char *)buf len:(int)len isNew:(int)isNew
|
|
|
1622 |
{
|
|
|
1623 |
int answer;
|
|
|
1624 |
NSSavePanel *sp;
|
|
|
1625 |
NSOpenPanel *op;
|
| 783 |
urbaneks |
1626 |
|
| 568 |
iacus |
1627 |
buf[0] = '\0';
|
|
|
1628 |
if(isNew==1){
|
|
|
1629 |
sp = [NSSavePanel savePanel];
|
| 917 |
urbaneks |
1630 |
[sp setTitle:NLS(@"Choose New File Name")];
|
| 568 |
iacus |
1631 |
answer = [sp runModalForDirectory:nil file:nil];
|
| 783 |
urbaneks |
1632 |
|
| 568 |
iacus |
1633 |
if(answer == NSOKButton) {
|
|
|
1634 |
if([sp filename] != nil){
|
| 1457 |
urbaneks |
1635 |
CFStringGetCString((CFStringRef)[sp filename], buf, len-1, kCFStringEncodingUTF8);
|
| 568 |
iacus |
1636 |
buf[len] = '\0';
|
|
|
1637 |
}
|
|
|
1638 |
}
|
|
|
1639 |
} else {
|
|
|
1640 |
op = [NSOpenPanel openPanel];
|
| 917 |
urbaneks |
1641 |
[op setTitle:NLS(@"Choose File")];
|
| 568 |
iacus |
1642 |
answer = [op runModalForDirectory:nil file:nil];
|
| 783 |
urbaneks |
1643 |
|
| 568 |
iacus |
1644 |
if(answer == NSOKButton) {
|
|
|
1645 |
if([op filename] != nil){
|
| 1457 |
urbaneks |
1646 |
CFStringGetCString((CFStringRef)[op filename], buf, len-1, kCFStringEncodingUTF8 );
|
| 568 |
iacus |
1647 |
buf[len] = '\0';
|
|
|
1648 |
}
|
|
|
1649 |
}
|
| 1457 |
urbaneks |
1650 |
}
|
|
|
1651 |
return strlen(buf); // is is used? it's potentially incorrect...
|
|
|
1652 |
}
|
| 568 |
iacus |
1653 |
|
| 789 |
urbaneks |
1654 |
- (void) loadFile:(NSString *)fname
|
| 568 |
iacus |
1655 |
{
|
| 1490 |
urbaneks |
1656 |
int res = [[RController sharedController] isImageData:fname];
|
| 568 |
iacus |
1657 |
|
|
|
1658 |
switch(res){
|
|
|
1659 |
case -1:
|
| 783 |
urbaneks |
1660 |
NSLog(@"cannot open file");
|
|
|
1661 |
break;
|
|
|
1662 |
|
| 568 |
iacus |
1663 |
case 0:
|
| 789 |
urbaneks |
1664 |
[self sendInput: [NSString stringWithFormat:@"load(\"%@\")",fname]];
|
| 783 |
urbaneks |
1665 |
break;
|
|
|
1666 |
|
| 568 |
iacus |
1667 |
case 1:
|
| 789 |
urbaneks |
1668 |
[self sendInput: [NSString stringWithFormat:@"source(\"%@\")",fname]];
|
| 783 |
urbaneks |
1669 |
break;
|
| 568 |
iacus |
1670 |
default:
|
| 783 |
urbaneks |
1671 |
break;
|
| 568 |
iacus |
1672 |
}
|
|
|
1673 |
}
|
|
|
1674 |
|
|
|
1675 |
// FIXME: is this really sufficient? what about compressed files?
|
|
|
1676 |
/* isImageData: returns -1 on error, 0 if the file is RDX2 or RDX1,
|
| 783 |
urbaneks |
1677 |
1 otherwise.
|
| 568 |
iacus |
1678 |
*/
|
| 789 |
urbaneks |
1679 |
- (int)isImageData:(NSString *)fname
|
| 568 |
iacus |
1680 |
{
|
| 789 |
urbaneks |
1681 |
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:fname];
|
|
|
1682 |
NSData *header;
|
|
|
1683 |
char buf[5];
|
|
|
1684 |
|
|
|
1685 |
if (!fh)
|
|
|
1686 |
return -1;
|
|
|
1687 |
|
|
|
1688 |
header = [fh readDataOfLength:4];
|
|
|
1689 |
[fh closeFile];
|
| 568 |
iacus |
1690 |
|
| 789 |
urbaneks |
1691 |
if (!header || [header length]<4)
|
|
|
1692 |
return -1;
|
|
|
1693 |
|
|
|
1694 |
memcpy(buf, [header bytes], 4);
|
|
|
1695 |
|
|
|
1696 |
buf[4]=0;
|
|
|
1697 |
if( (strcmp(buf,"RDX2")==0) || ((strcmp(buf,"RDX1")==0)))
|
|
|
1698 |
return(0);
|
|
|
1699 |
return(1);
|
| 568 |
iacus |
1700 |
}
|
|
|
1701 |
|
|
|
1702 |
- (void) doProcessEvents: (BOOL) blocking {
|
|
|
1703 |
NSEvent *event;
|
| 783 |
urbaneks |
1704 |
|
| 568 |
iacus |
1705 |
if (blocking){
|
|
|
1706 |
event = [NSApp nextEventMatchingMask:NSAnyEventMask
|
|
|
1707 |
untilDate:[NSDate distantFuture]
|
|
|
1708 |
inMode:NSDefaultRunLoopMode
|
|
|
1709 |
dequeue:YES];
|
|
|
1710 |
[NSApp sendEvent:event];
|
|
|
1711 |
} else {
|
|
|
1712 |
while( (event = [NSApp nextEventMatchingMask:NSAnyEventMask
|
|
|
1713 |
untilDate:[NSDate dateWithTimeIntervalSinceNow:0.0001]
|
|
|
1714 |
inMode:NSDefaultRunLoopMode
|
|
|
1715 |
dequeue:YES]))
|
|
|
1716 |
[NSApp sendEvent:event];
|
|
|
1717 |
}
|
|
|
1718 |
return;
|
|
|
1719 |
}
|
|
|
1720 |
|
|
|
1721 |
- (void) handleProcessEvents{
|
|
|
1722 |
[self doProcessEvents: NO];
|
|
|
1723 |
}
|
|
|
1724 |
|
| 783 |
urbaneks |
1725 |
|
| 568 |
iacus |
1726 |
/*
|
|
|
1727 |
This method calls the showHelpFor method of the Help Manager which opens
|
| 783 |
urbaneks |
1728 |
the internal html browser/help system of R.app
|
|
|
1729 |
This method is called from ReadConsole.
|
| 568 |
iacus |
1730 |
|
| 783 |
urbaneks |
1731 |
The input C string 'topic' is parsed and the behaviour is the following:
|
|
|
1732 |
|
|
|
1733 |
topic = ?something => showHelpFor:@"something"
|
|
|
1734 |
topic = help(something) => showHelpFor:@"something"
|
|
|
1735 |
topic = help(something); print(anotherthing); => showHelpFor:@"something"
|
|
|
1736 |
|
|
|
1737 |
which means that all the rest of the input is discarded.
|
|
|
1738 |
No error message or warning are raised.
|
|
|
1739 |
*/
|
| 568 |
iacus |
1740 |
|
|
|
1741 |
- (void) openHelpFor: (char *) topic
|
|
|
1742 |
{
|
|
|
1743 |
char tmp[300];
|
|
|
1744 |
int i;
|
| 783 |
urbaneks |
1745 |
|
| 1136 |
urbaneks |
1746 |
// NSLog(@"openHelpFor: <%@>", [NSString stringWithCString:topic]);
|
|
|
1747 |
if(topic[0] == '?' && (strlen(topic)>2))
|
|
|
1748 |
[[HelpManager sharedController] showHelpFor:
|
|
|
1749 |
[NSString stringWithCString:topic+1 length:strlen(topic)-2]];
|
| 568 |
iacus |
1750 |
if(strncmp("help(",topic,5)==0){
|
|
|
1751 |
for(i=5;i<strlen(topic); i++){
|
|
|
1752 |
if(topic[i]==')')
|
|
|
1753 |
break;
|
|
|
1754 |
tmp[i-5] = topic[i];
|
|
|
1755 |
}
|
|
|
1756 |
tmp[i-5] = '\0';
|
| 796 |
urbaneks |
1757 |
[[HelpManager sharedController] showHelpFor: [NSString stringWithCString:tmp]];
|
| 568 |
iacus |
1758 |
}
|
|
|
1759 |
}
|
|
|
1760 |
|
|
|
1761 |
- (void) setupToolbar {
|
| 783 |
urbaneks |
1762 |
|
| 568 |
iacus |
1763 |
// Create a new toolbar instance, and attach it to our document window
|
| 783 |
urbaneks |
1764 |
toolbar = [[[NSToolbar alloc] initWithIdentifier: RToolbarIdentifier] autorelease];
|
| 568 |
iacus |
1765 |
|
|
|
1766 |
// Set up toolbar properties: Allow customization, give a default display mode, and remember state in user defaults
|
|
|
1767 |
[toolbar setAllowsUserCustomization: YES];
|
|
|
1768 |
[toolbar setAutosavesConfiguration: YES];
|
|
|
1769 |
[toolbar setDisplayMode: NSToolbarDisplayModeIconOnly];
|
|
|
1770 |
|
|
|
1771 |
// We are the delegate
|
|
|
1772 |
[toolbar setDelegate: self];
|
|
|
1773 |
|
|
|
1774 |
// Attach the toolbar to the document window
|
|
|
1775 |
[RConsoleWindow setToolbar: toolbar];
|
|
|
1776 |
}
|
|
|
1777 |
|
|
|
1778 |
- (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
|
|
|
1779 |
// Required delegate method: Given an item identifier, this method returns an item
|
|
|
1780 |
// The toolbar will use this method to obtain toolbar items that can be displayed in the customization sheet, or in the toolbar itself
|
|
|
1781 |
NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
|
|
|
1782 |
|
|
|
1783 |
if ([itemIdent isEqual: SaveDocToolbarItemIdentifier]) {
|
| 577 |
urbaneks |
1784 |
// Set the text label to be displayed in the toolbar and customization palette
|
| 1045 |
urbaneks |
1785 |
[toolbarItem setLabel: NLS(@"Save")];
|
|
|
1786 |
[toolbarItem setPaletteLabel: NLS(@"Save Console Window")];
|
|
|
1787 |
[toolbarItem setToolTip: NLS(@"Save R console window")];
|
| 577 |
urbaneks |
1788 |
[toolbarItem setImage: [NSImage imageNamed: @"SaveDocumentItemImage"]];
|
|
|
1789 |
[toolbarItem setTarget: self];
|
|
|
1790 |
[toolbarItem setAction: @selector(saveDocument:)];
|
|
|
1791 |
} else if ([itemIdent isEqual: NewEditWinToolbarItemIdentifier]) {
|
|
|
1792 |
// Set the text label to be displayed in the toolbar and customization palette
|
| 1045 |
urbaneks |
1793 |
[toolbarItem setLabel: NLS(@"New Document")];
|
|
|
1794 |
[toolbarItem setPaletteLabel: NLS(@"New Document")];
|
|
|
1795 |
[toolbarItem setToolTip: NLS(@"Create a new, empty document in the editor")];
|
| 577 |
urbaneks |
1796 |
[toolbarItem setImage: [NSImage imageNamed: @"emptyDoc"]];
|
|
|
1797 |
[toolbarItem setTarget: self];
|
|
|
1798 |
[toolbarItem setAction: @selector(newDocument:)];
|
| 783 |
urbaneks |
1799 |
|
| 568 |
iacus |
1800 |
} else if ([itemIdent isEqual: X11ToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1801 |
[toolbarItem setLabel: NLS(@"Start X11")];
|
|
|
1802 |
[toolbarItem setPaletteLabel: NLS(@"Start X11 Server")];
|
|
|
1803 |
[toolbarItem setToolTip: NLS(@"Start the X11 window server to allow R using X11 device and Tcl/Tk")];
|
| 577 |
urbaneks |
1804 |
[toolbarItem setImage: [NSImage imageNamed: @"X11"]];
|
|
|
1805 |
[toolbarItem setTarget: self];
|
|
|
1806 |
[toolbarItem setAction: @selector(runX11:)];
|
| 783 |
urbaneks |
1807 |
|
| 568 |
iacus |
1808 |
} else if ([itemIdent isEqual: SetColorsToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1809 |
[toolbarItem setLabel: NLS(@"Set Colors")];
|
|
|
1810 |
[toolbarItem setPaletteLabel: NLS(@"Set R Colors")];
|
|
|
1811 |
[toolbarItem setToolTip: NLS(@"Set R console colors")];
|
| 568 |
iacus |
1812 |
[toolbarItem setImage: [NSImage imageNamed: @"colors"]];
|
|
|
1813 |
[toolbarItem setTarget: self];
|
|
|
1814 |
[toolbarItem setAction: @selector(openColors:)];
|
| 783 |
urbaneks |
1815 |
|
| 568 |
iacus |
1816 |
} else if ([itemIdent isEqual: LoadFileInEditorToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1817 |
[toolbarItem setLabel: NLS(@"Open In Editor")];
|
|
|
1818 |
[toolbarItem setPaletteLabel: NLS(@"Open In Editor")];
|
|
|
1819 |
[toolbarItem setToolTip: NLS(@"Open document in editor")];
|
| 577 |
urbaneks |
1820 |
[toolbarItem setImage: [NSImage imageNamed: @"RDoc"]];
|
|
|
1821 |
[toolbarItem setTarget: self];
|
|
|
1822 |
[toolbarItem setAction: @selector(openDocument:)];
|
| 783 |
urbaneks |
1823 |
|
| 568 |
iacus |
1824 |
} else if ([itemIdent isEqual: SourceRCodeToolbarIdentifier]) {
|
| 1045 |
urbaneks |
1825 |
[toolbarItem setLabel: NLS(@"Source/Load")];
|
|
|
1826 |
[toolbarItem setPaletteLabel: NLS(@"Source or Load in R")];
|
|
|
1827 |
[toolbarItem setToolTip: NLS(@"Source script or load data in R")];
|
| 577 |
urbaneks |
1828 |
[toolbarItem setImage: [NSImage imageNamed: @"sourceR"]];
|
|
|
1829 |
[toolbarItem setTarget: self];
|
| 626 |
iacus |
1830 |
[toolbarItem setAction: @selector(sourceOrLoadFile:)];
|
| 783 |
urbaneks |
1831 |
|
| 568 |
iacus |
1832 |
} else if([itemIdent isEqual: NewQuartzToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1833 |
[toolbarItem setLabel: NLS(@"Quartz")];
|
|
|
1834 |
[toolbarItem setPaletteLabel: NLS(@"Quartz")];
|
|
|
1835 |
[toolbarItem setToolTip: NLS(@"Open a new Quartz device window")];
|
| 577 |
urbaneks |
1836 |
[toolbarItem setImage: [NSImage imageNamed: @"quartz"]];
|
|
|
1837 |
[toolbarItem setTarget: self];
|
|
|
1838 |
[toolbarItem setAction: @selector(newQuartzDevice:) ];
|
| 783 |
urbaneks |
1839 |
|
| 568 |
iacus |
1840 |
} else if([itemIdent isEqual: InterruptToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1841 |
[toolbarItem setLabel: NLS(@"Stop")];
|
|
|
1842 |
[toolbarItem setPaletteLabel: NLS(@"Stop")];
|
| 577 |
urbaneks |
1843 |
toolbarStopItem = toolbarItem;
|
| 1045 |
urbaneks |
1844 |
[toolbarItem setToolTip: NLS(@"Interrupt current R computation")];
|
| 577 |
urbaneks |
1845 |
[toolbarItem setImage: [NSImage imageNamed: @"stop"]];
|
|
|
1846 |
[toolbarItem setTarget: self];
|
|
|
1847 |
[toolbarItem setAction: @selector(breakR:) ];
|
| 783 |
urbaneks |
1848 |
|
| 568 |
iacus |
1849 |
} else if([itemIdent isEqual: FontSizeToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1850 |
[toolbarItem setLabel: NLS(@"Font Size")];
|
|
|
1851 |
[toolbarItem setPaletteLabel: NLS(@"Font Size")];
|
|
|
1852 |
[toolbarItem setToolTip: NLS(@"Change the size of R console font")];
|
| 577 |
urbaneks |
1853 |
[toolbarItem setTarget: self];
|
|
|
1854 |
[toolbarItem performSelector:@selector(setView:) withObject:fontSizeView];
|
|
|
1855 |
[toolbarItem setAction:NULL];
|
|
|
1856 |
[toolbarItem setView:fontSizeView];
|
|
|
1857 |
if ([toolbarItem view]!=NULL)
|
|
|
1858 |
{
|
|
|
1859 |
[toolbarItem setMinSize:[[toolbarItem view] bounds].size];
|
|
|
1860 |
[toolbarItem setMaxSize:[[toolbarItem view] bounds].size];
|
|
|
1861 |
}
|
| 783 |
urbaneks |
1862 |
|
| 568 |
iacus |
1863 |
} else if([itemIdent isEqual: NewQuartzToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1864 |
[toolbarItem setLabel: NLS(@"Quartz")];
|
|
|
1865 |
[toolbarItem setPaletteLabel: NLS(@"Quartz")];
|
|
|
1866 |
[toolbarItem setToolTip: NLS(@"Open a new Quartz device window")];
|
| 577 |
urbaneks |
1867 |
[toolbarItem setImage: [NSImage imageNamed: @"quartz"]];
|
|
|
1868 |
[toolbarItem setTarget: self];
|
|
|
1869 |
[toolbarItem setAction: @selector(newQuartzDevice:) ];
|
| 783 |
urbaneks |
1870 |
|
| 568 |
iacus |
1871 |
} else if([itemIdent isEqual: ShowHistoryToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1872 |
[toolbarItem setLabel: NLS(@"History")];
|
|
|
1873 |
[toolbarItem setPaletteLabel: NLS(@"History")];
|
|
|
1874 |
[toolbarItem setToolTip: NLS(@"Show/Hide R command history")];
|
| 577 |
urbaneks |
1875 |
[toolbarItem setImage: [NSImage imageNamed: @"history"]];
|
|
|
1876 |
[toolbarItem setTarget: self];
|
| 1136 |
urbaneks |
1877 |
[toolbarItem setAction: @selector(toggleHistory:) ];
|
| 568 |
iacus |
1878 |
} else if([itemIdent isEqual: AuthenticationToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1879 |
[toolbarItem setLabel: NLS(@"Authentication")];
|
|
|
1880 |
[toolbarItem setPaletteLabel: NLS(@"Authentication")];
|
|
|
1881 |
[toolbarItem setToolTip: NLS(@"Authorize R to run system commands as root")];
|
| 577 |
urbaneks |
1882 |
[toolbarItem setImage: [NSImage imageNamed: @"lock-locked"]];
|
|
|
1883 |
[toolbarItem setTarget: self];
|
|
|
1884 |
[toolbarItem setAction: @selector(toggleAuthentication:) ];
|
| 783 |
urbaneks |
1885 |
|
| 568 |
iacus |
1886 |
} else if([itemIdent isEqual: QuitRToolbarItemIdentifier]) {
|
| 1045 |
urbaneks |
1887 |
[toolbarItem setLabel: NLS(@"Quit")];
|
|
|
1888 |
[toolbarItem setPaletteLabel: NLS(@"Quit")];
|
|
|
1889 |
[toolbarItem setToolTip: NLS(@"Quit R")];
|
| 577 |
urbaneks |
1890 |
[toolbarItem setImage: [NSImage imageNamed: @"quit"]];
|
|
|
1891 |
[toolbarItem setTarget: self];
|
|
|
1892 |
[toolbarItem setAction: @selector(quitR:) ];
|
| 783 |
urbaneks |
1893 |
|
| 568 |
iacus |
1894 |
} else {
|
| 783 |
urbaneks |
1895 |
// itemIdent refered to a toolbar item that is not provide or supported by us or cocoa
|
|
|
1896 |
// Returning nil will inform the toolbar this kind of item is not supported
|
|
|
1897 |
toolbarItem = nil;
|
| 568 |
iacus |
1898 |
}
|
|
|
1899 |
return toolbarItem;
|
|
|
1900 |
}
|
|
|
1901 |
|
|
|
1902 |
|
|
|
1903 |
- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar {
|
|
|
1904 |
// Required delegate method: Returns the ordered list of items to be shown in the toolbar by default
|
|
|
1905 |
// If during the toolbar's initialization, no overriding values are found in the user defaults, or if the
|
|
|
1906 |
// user chooses to revert to the default items this set will be used
|
|
|
1907 |
return [NSArray arrayWithObjects: InterruptToolbarItemIdentifier, SourceRCodeToolbarIdentifier,
|
| 577 |
urbaneks |
1908 |
NewQuartzToolbarItemIdentifier, X11ToolbarItemIdentifier,
|
|
|
1909 |
NSToolbarSeparatorItemIdentifier,
|
|
|
1910 |
AuthenticationToolbarItemIdentifier, ShowHistoryToolbarItemIdentifier,
|
|
|
1911 |
SetColorsToolbarItemIdentifier,
|
|
|
1912 |
NSToolbarSeparatorItemIdentifier, /* SaveDocToolbarItemIdentifier, */
|
|
|
1913 |
LoadFileInEditorToolbarItemIdentifier,
|
|
|
1914 |
NewEditWinToolbarItemIdentifier, NSToolbarPrintItemIdentifier,
|
|
|
1915 |
NSToolbarSeparatorItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
|
|
|
1916 |
QuitRToolbarItemIdentifier, nil];
|
| 568 |
iacus |
1917 |
}
|
|
|
1918 |
|
|
|
1919 |
- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar {
|
|
|
1920 |
// Required delegate method: Returns the list of all allowed items by identifier. By default, the toolbar
|
|
|
1921 |
// does not assume any items are allowed, even the separator. So, every allowed item must be explicitly listed
|
|
|
1922 |
// The set of allowed items is used to construct the customization palette
|
|
|
1923 |
return [NSArray arrayWithObjects: QuitRToolbarItemIdentifier, AuthenticationToolbarItemIdentifier, ShowHistoryToolbarItemIdentifier,
|
| 577 |
urbaneks |
1924 |
InterruptToolbarItemIdentifier, NewQuartzToolbarItemIdentifier, /* SaveDocToolbarItemIdentifier, */
|
|
|
1925 |
NewEditWinToolbarItemIdentifier, LoadFileInEditorToolbarItemIdentifier,
|
|
|
1926 |
NSToolbarPrintItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier,
|
|
|
1927 |
NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier,
|
|
|
1928 |
NSToolbarSeparatorItemIdentifier, X11ToolbarItemIdentifier,
|
|
|
1929 |
SetColorsToolbarItemIdentifier,
|
|
|
1930 |
FontSizeToolbarItemIdentifier, SourceRCodeToolbarIdentifier, nil];
|
| 568 |
iacus |
1931 |
}
|
|
|
1932 |
|
|
|
1933 |
- (void) toolbarWillAddItem: (NSNotification *) notif {
|
|
|
1934 |
// Optional delegate method: Before an new item is added to the toolbar, this notification is posted.
|
|
|
1935 |
// This is the best place to notice a new item is going into the toolbar. For instance, if you need to
|
|
|
1936 |
// cache a reference to the toolbar item or need to set up some initial state, this is the best place
|
|
|
1937 |
// to do it. The notification object is the toolbar to which the item is being added. The item being
|
|
|
1938 |
// added is found by referencing the @"item" key in the userInfo
|
|
|
1939 |
NSToolbarItem *addedItem = [[notif userInfo] objectForKey: @"item"];
|
|
|
1940 |
if ([[addedItem itemIdentifier] isEqual: NSToolbarPrintItemIdentifier]) {
|
| 1045 |
urbaneks |
1941 |
[addedItem setToolTip: NLS(@"Print this document")];
|
| 568 |
iacus |
1942 |
[addedItem setTarget: self];
|
|
|
1943 |
}
|
|
|
1944 |
}
|
|
|
1945 |
|
|
|
1946 |
- (void) toolbarDidRemoveItem: (NSNotification *) notif {
|
|
|
1947 |
// Optional delegate method: After an item is removed from a toolbar, this notification is sent. This allows
|
|
|
1948 |
// the chance to tear down information related to the item that may have been cached. The notification object
|
|
|
1949 |
// is the toolbar from which the item is being removed. The item being added is found by referencing the @"item"
|
|
|
1950 |
// key in the userInfo
|
| 783 |
urbaneks |
1951 |
// NSToolbarItem *removedItem = [[notif userInfo] objectForKey: @"item"];
|
| 568 |
iacus |
1952 |
}
|
|
|
1953 |
|
|
|
1954 |
- (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem {
|
|
|
1955 |
// Optional method: This message is sent to us since we are the target of some toolbar item actions
|
|
|
1956 |
// (for example: of the save items action)
|
|
|
1957 |
BOOL enable = NO;
|
|
|
1958 |
if ([[toolbarItem itemIdentifier] isEqual: SaveDocToolbarItemIdentifier]) {
|
|
|
1959 |
enable = [RConsoleWindow isDocumentEdited];
|
|
|
1960 |
} else if ([[toolbarItem itemIdentifier] isEqual: SourceRCodeToolbarIdentifier]) {
|
|
|
1961 |
enable = YES;
|
|
|
1962 |
} else if ([[toolbarItem itemIdentifier] isEqual: X11ToolbarItemIdentifier]) {
|
|
|
1963 |
enable = YES;
|
|
|
1964 |
} else if ([[toolbarItem itemIdentifier] isEqual: SetColorsToolbarItemIdentifier]) {
|
|
|
1965 |
enable = YES;
|
|
|
1966 |
} else if ([[toolbarItem itemIdentifier] isEqual: LoadFileInEditorToolbarItemIdentifier]) {
|
|
|
1967 |
enable = YES;
|
|
|
1968 |
} else if ([[toolbarItem itemIdentifier] isEqual: NewEditWinToolbarItemIdentifier]) {
|
|
|
1969 |
enable = YES;
|
|
|
1970 |
} else if ([[toolbarItem itemIdentifier] isEqual: NSToolbarPrintItemIdentifier]) {
|
|
|
1971 |
enable = YES;
|
|
|
1972 |
} else if ([[toolbarItem itemIdentifier] isEqual: NewQuartzToolbarItemIdentifier]) {
|
|
|
1973 |
enable = YES;
|
|
|
1974 |
} else if ([[toolbarItem itemIdentifier] isEqual: InterruptToolbarItemIdentifier]) {
|
|
|
1975 |
enable = (busyRFlag || (childPID>0));
|
|
|
1976 |
} else if ([[toolbarItem itemIdentifier] isEqual: ShowHistoryToolbarItemIdentifier]) {
|
|
|
1977 |
enable = YES;
|
|
|
1978 |
} else if ([[toolbarItem itemIdentifier] isEqual: AuthenticationToolbarItemIdentifier]) {
|
|
|
1979 |
enable = YES;
|
|
|
1980 |
} else if ([[toolbarItem itemIdentifier] isEqual: QuitRToolbarItemIdentifier]) {
|
|
|
1981 |
enable = YES;
|
|
|
1982 |
}
|
|
|
1983 |
return enable;
|
|
|
1984 |
}
|
|
|
1985 |
|
|
|
1986 |
|
|
|
1987 |
/* This is needed to force the NSDocument to know when edited windows are dirty */
|
|
|
1988 |
- (void) RConsoleDidResize: (NSNotification *)notification{
|
|
|
1989 |
[self setOptionWidth:NO];
|
|
|
1990 |
}
|
|
|
1991 |
|
|
|
1992 |
- (void) setOptionWidth:(BOOL)force;
|
|
|
1993 |
{
|
|
|
1994 |
float newSize = [[RTextView textContainer] containerSize].width;
|
|
|
1995 |
float newFontSize = [[RTextView font] pointSize];
|
|
|
1996 |
if((newSize != currentSize) | (newFontSize != currentFontSize) | force){
|
|
|
1997 |
float newConsoleWidth = 1.5*newSize/newFontSize-1.0;
|
|
|
1998 |
if((int)newConsoleWidth != (int)currentConsoleWidth){
|
|
|
1999 |
R_SetOptionWidth(newConsoleWidth);
|
|
|
2000 |
currentSize = newSize;
|
|
|
2001 |
currentFontSize = newFontSize;
|
|
|
2002 |
currentConsoleWidth = newConsoleWidth;
|
|
|
2003 |
}
|
|
|
2004 |
}
|
|
|
2005 |
}
|
|
|
2006 |
|
|
|
2007 |
-(IBAction) checkForUpdates:(id)sender{
|
|
|
2008 |
[[REngine mainEngine] executeString: @"Rapp.updates()"];
|
|
|
2009 |
}
|
|
|
2010 |
|
|
|
2011 |
-(IBAction) getWorkingDir:(id)sender
|
|
|
2012 |
{
|
|
|
2013 |
[self sendInput:@"getwd()"];
|
| 783 |
urbaneks |
2014 |
// [[REngine mainEngine] evaluateString: @"getwd()"];
|
|
|
2015 |
|
| 568 |
iacus |
2016 |
}
|
|
|
2017 |
|
|
|
2018 |
-(IBAction) resetWorkingDir:(id)sender
|
|
|
2019 |
{
|
| 820 |
urbaneks |
2020 |
[[NSFileManager defaultManager] changeCurrentDirectoryPath: [[Preferences stringForKey:@"initialWorkingDirectoryKey" withDefault:@"~"] stringByExpandingTildeInPath]];
|
|
|
2021 |
[self showWorkingDir:sender];
|
| 568 |
iacus |
2022 |
}
|
|
|
2023 |
|
|
|
2024 |
-(IBAction) setWorkingDir:(id)sender
|
|
|
2025 |
{
|
|
|
2026 |
NSOpenPanel *op;
|
|
|
2027 |
int answer;
|
| 872 |
urbaneks |
2028 |
|
| 568 |
iacus |
2029 |
op = [NSOpenPanel openPanel];
|
|
|
2030 |
[op setCanChooseDirectories:YES];
|
|
|
2031 |
[op setCanChooseFiles:NO];
|
| 1045 |
urbaneks |
2032 |
[op setTitle:NLS(@"Choose New Working Directory")];
|
| 783 |
urbaneks |
2033 |
|
| 820 |
urbaneks |
2034 |
answer = [op runModalForDirectory:[[NSFileManager defaultManager] currentDirectoryPath] file:nil types:[NSArray arrayWithObject:@""]];
|
| 783 |
urbaneks |
2035 |
|
| 820 |
urbaneks |
2036 |
if(answer == NSOKButton && [op directory] != nil)
|
|
|
2037 |
[[NSFileManager defaultManager] changeCurrentDirectoryPath:[[op directory] stringByExpandingTildeInPath]];
|
|
|
2038 |
[self showWorkingDir:sender];
|
| 568 |
iacus |
2039 |
}
|
|
|
2040 |
|
|
|
2041 |
- (IBAction) showWorkingDir:(id)sender
|
|
|
2042 |
{
|
|
|
2043 |
[WDirView setEditable:YES];
|
| 820 |
urbaneks |
2044 |
[WDirView setStringValue: [[[NSFileManager defaultManager] currentDirectoryPath] stringByAbbreviatingWithTildeInPath]];
|
| 568 |
iacus |
2045 |
[WDirView setEditable:NO];
|
|
|
2046 |
}
|
|
|
2047 |
|
|
|
2048 |
|
|
|
2049 |
|
|
|
2050 |
- (IBAction)installFromDir:(id)sender
|
|
|
2051 |
{
|
|
|
2052 |
NSOpenPanel *op;
|
|
|
2053 |
int answer;
|
|
|
2054 |
|
|
|
2055 |
op = [NSOpenPanel openPanel];
|
|
|
2056 |
[op setCanChooseDirectories:YES];
|
|
|
2057 |
[op setCanChooseFiles:NO];
|
| 1045 |
urbaneks |
2058 |
[op setTitle:NLS(@"Select Package Directory")];
|
| 568 |
iacus |
2059 |
|
|
|
2060 |
answer = [op runModalForDirectory:nil file:nil types:[NSArray arrayWithObject:@""]];
|
|
|
2061 |
[op setCanChooseDirectories:NO];
|
|
|
2062 |
[op setCanChooseFiles:YES];
|
| 783 |
urbaneks |
2063 |
|
| 568 |
iacus |
2064 |
if(answer == NSOKButton)
|
|
|
2065 |
if([op directory] != nil)
|
|
|
2066 |
[[REngine mainEngine] executeString: [NSString stringWithFormat:@"install.from.file(pkg=\"%@\")",[op directory]] ];
|
|
|
2067 |
}
|
|
|
2068 |
|
|
|
2069 |
- (IBAction)installFromBinary:(id)sender
|
|
|
2070 |
{
|
|
|
2071 |
[[REngine mainEngine] executeString: @"install.from.file(binary=TRUE)" ];
|
|
|
2072 |
}
|
|
|
2073 |
|
|
|
2074 |
- (IBAction)installFromSource:(id)sender
|
|
|
2075 |
{
|
|
|
2076 |
[[REngine mainEngine] executeString:@"install.from.file(binary=FALSE)" ];
|
|
|
2077 |
}
|
|
|
2078 |
|
|
|
2079 |
- (IBAction)togglePackageInstaller:(id)sender
|
|
|
2080 |
{
|
| 688 |
urbaneks |
2081 |
[[PackageInstaller sharedController] show];
|
| 568 |
iacus |
2082 |
}
|
|
|
2083 |
|
|
|
2084 |
- (IBAction)toggleWSBrowser:(id)sender
|
|
|
2085 |
{
|
|
|
2086 |
[WSBrowser toggleWorkspaceBrowser];
|
|
|
2087 |
[[REngine mainEngine] executeString:@"browseEnv(html=F)"];
|
| 783 |
urbaneks |
2088 |
|
| 568 |
iacus |
2089 |
}
|
|
|
2090 |
|
|
|
2091 |
- (IBAction)loadWorkSpace:(id)sender
|
|
|
2092 |
{
|
|
|
2093 |
[self sendInput:@"load(\".RData\")"];
|
| 783 |
urbaneks |
2094 |
// [[REngine mainEngine] evaluateString:@"load(\".RData\")" ];
|
|
|
2095 |
|
| 568 |
iacus |
2096 |
}
|
|
|
2097 |
|
|
|
2098 |
- (IBAction)saveWorkSpace:(id)sender
|
|
|
2099 |
{
|
|
|
2100 |
[self sendInput:@"save.image()"];
|
| 783 |
urbaneks |
2101 |
// [[REngine mainEngine] evaluateString:@"save.image()"];
|
| 568 |
iacus |
2102 |
|
|
|
2103 |
}
|
|
|
2104 |
|
|
|
2105 |
- (IBAction)loadWorkSpaceFile:(id)sender
|
|
|
2106 |
{
|
|
|
2107 |
[[REngine mainEngine] executeString:@"load(file.choose())"];
|
|
|
2108 |
}
|
|
|
2109 |
|
|
|
2110 |
- (IBAction)saveWorkSpaceFile:(id)sender
|
|
|
2111 |
{
|
| 622 |
iacus |
2112 |
[[REngine mainEngine] executeString: @"save.image(file=file.choose(TRUE))"];
|
| 568 |
iacus |
2113 |
}
|
|
|
2114 |
|
|
|
2115 |
- (IBAction)showWorkSpace:(id)sender{
|
|
|
2116 |
[self sendInput:@"ls()"];
|
|
|
2117 |
}
|
|
|
2118 |
|
|
|
2119 |
- (IBAction)clearWorkSpace:(id)sender
|
|
|
2120 |
{
|
| 1045 |
urbaneks |
2121 |
NSBeginAlertSheet(NLS(@"Clear Workspace"), NLS(@"Yes"), NLS(@"No") , nil, RConsoleWindow, self, @selector(shouldClearWS:returnCode:contextInfo:), NULL, NULL,
|
|
|
2122 |
NLS(@"All objects in the workspace will be removed. Are you sure you want to proceed?"));
|
| 568 |
iacus |
2123 |
}
|
|
|
2124 |
|
|
|
2125 |
/* this gets called by the "wanna save?" sheet on window close */
|
|
|
2126 |
- (void) shouldClearWS:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
|
|
2127 |
if (returnCode==NSAlertDefaultReturn)
|
|
|
2128 |
[[REngine mainEngine] executeString: @"rm(list=ls())"];
|
|
|
2129 |
}
|
|
|
2130 |
|
|
|
2131 |
- (IBAction)togglePackageManager:(id)sender
|
|
|
2132 |
{
|
| 688 |
urbaneks |
2133 |
if ([[PackageManager sharedController] count]==0)
|
| 568 |
iacus |
2134 |
[[REngine mainEngine] executeString:@"package.manager()"];
|
| 688 |
urbaneks |
2135 |
else
|
|
|
2136 |
[[PackageManager sharedController] show];
|
| 568 |
iacus |
2137 |
}
|
|
|
2138 |
|
|
|
2139 |
- (IBAction)toggleDataManager:(id)sender
|
|
|
2140 |
{
|
| 688 |
urbaneks |
2141 |
if ([[DataManager sharedController] count]==0) {
|
|
|
2142 |
[[DataManager sharedController] show];
|
| 568 |
iacus |
2143 |
[[REngine mainEngine] executeString: @"data.manager()"];
|
| 688 |
urbaneks |
2144 |
} else
|
|
|
2145 |
[[DataManager sharedController] show];
|
| 568 |
iacus |
2146 |
}
|
|
|
2147 |
|
|
|
2148 |
|
|
|
2149 |
-(IBAction) runX11:(id)sender{
|
| 577 |
urbaneks |
2150 |
system("open -a X11.app");
|
| 568 |
iacus |
2151 |
}
|
| 783 |
urbaneks |
2152 |
|
| 568 |
iacus |
2153 |
-(IBAction) openColors:(id)sender{
|
| 815 |
urbaneks |
2154 |
[prefsCtrl selectPaneWithIdentifier:@"Colors"];
|
|
|
2155 |
[prefsCtrl showWindow:self];
|
|
|
2156 |
[[prefsCtrl window] makeKeyAndOrderFront:self];
|
| 568 |
iacus |
2157 |
}
|
|
|
2158 |
|
|
|
2159 |
- (IBAction)performHelpSearch:(id)sender {
|
|
|
2160 |
if ([[sender stringValue] length]>0) {
|
| 1136 |
urbaneks |
2161 |
[[HelpManager sharedController] showHelpFor:[sender stringValue]];
|
| 568 |
iacus |
2162 |
[helpSearch setStringValue:@""];
|
|
|
2163 |
}
|
|
|
2164 |
}
|
|
|
2165 |
|
| 626 |
iacus |
2166 |
- (IBAction)sourceOrLoadFile:(id)sender
|
|
|
2167 |
{
|
|
|
2168 |
int answer;
|
|
|
2169 |
NSOpenPanel *op;
|
|
|
2170 |
op = [NSOpenPanel openPanel];
|
| 1045 |
urbaneks |
2171 |
[op setTitle:NLS(@"R File to Source/Load")];
|
| 626 |
iacus |
2172 |
answer = [op runModalForTypes:nil];
|
|
|
2173 |
|
|
|
2174 |
if (answer==NSOKButton)
|
| 789 |
urbaneks |
2175 |
[self loadFile:[op filename]];
|
| 626 |
iacus |
2176 |
}
|
|
|
2177 |
|
| 577 |
urbaneks |
2178 |
- (IBAction)sourceFile:(id)sender
|
|
|
2179 |
{
|
|
|
2180 |
int answer;
|
|
|
2181 |
NSOpenPanel *op;
|
|
|
2182 |
op = [NSOpenPanel openPanel];
|
| 1045 |
urbaneks |
2183 |
[op setTitle:NLS(@"R File to Source")];
|
| 577 |
urbaneks |
2184 |
answer = [op runModalForTypes:nil];
|
| 783 |
urbaneks |
2185 |
|
| 582 |
urbaneks |
2186 |
if (answer==NSOKButton)
|
|
|
2187 |
[self sendInput:[NSString stringWithFormat:@"source(\"%@\")",[op filename]]];
|
| 577 |
urbaneks |
2188 |
}
|
| 568 |
iacus |
2189 |
|
|
|
2190 |
- (IBAction)printDocument:(id)sender
|
|
|
2191 |
{
|
|
|
2192 |
NSPrintInfo *printInfo;
|
|
|
2193 |
NSPrintInfo *sharedInfo;
|
|
|
2194 |
NSPrintOperation *printOp;
|
|
|
2195 |
NSMutableDictionary *printInfoDict;
|
|
|
2196 |
NSMutableDictionary *sharedDict;
|
|
|
2197 |
|
|
|
2198 |
sharedInfo = [NSPrintInfo sharedPrintInfo];
|
|
|
2199 |
sharedDict = [sharedInfo dictionary];
|
|
|
2200 |
printInfoDict = [NSMutableDictionary dictionaryWithDictionary:
|
|
|
2201 |
sharedDict];
|
|
|
2202 |
|
|
|
2203 |
printInfo = [[NSPrintInfo alloc] initWithDictionary: printInfoDict];
|
|
|
2204 |
[printInfo setHorizontalPagination: NSAutoPagination];
|
|
|
2205 |
[printInfo setVerticalPagination: NSAutoPagination];
|
|
|
2206 |
[printInfo setVerticallyCentered:NO];
|
|
|
2207 |
|
|
|
2208 |
printOp = [NSPrintOperation printOperationWithView:RTextView
|
|
|
2209 |
printInfo:printInfo];
|
|
|
2210 |
[printOp setShowPanels:YES];
|
|
|
2211 |
[printOp runOperation];
|
|
|
2212 |
}
|
|
|
2213 |
|
|
|
2214 |
- (IBAction) setDefaultColors:(id)sender {
|
| 815 |
urbaneks |
2215 |
int i = 0, ccs = [consoleColorsKeys count];
|
|
|
2216 |
[[Preferences sharedPreferences] beginBatch];
|
|
|
2217 |
while (i<ccs) {
|
|
|
2218 |
[Preferences setKey:[consoleColorsKeys objectAtIndex:i] withArchivedObject:[defaultConsoleColors objectAtIndex: i]];
|
|
|
2219 |
i++;
|
|
|
2220 |
}
|
|
|
2221 |
[[Preferences sharedPreferences] endBatch];
|
| 568 |
iacus |
2222 |
}
|
|
|
2223 |
|
| 815 |
urbaneks |
2224 |
- (void) updatePreferences {
|
| 1328 |
urbaneks |
2225 |
SLog(@"RController.updatePreferences");
|
| 815 |
urbaneks |
2226 |
currentFontSize = [Preferences floatForKey: FontSizeKey withDefault: 11.0];
|
| 898 |
urbaneks |
2227 |
NSFont *newFont = [NSFont userFixedPitchFontOfSize:currentFontSize];
|
|
|
2228 |
if (newFont!=textFont) {
|
| 1328 |
urbaneks |
2229 |
SLog(@" - releasing %@", textFont);
|
| 898 |
urbaneks |
2230 |
[textFont release];
|
| 1328 |
urbaneks |
2231 |
SLog(@" - using new %@", textFont);
|
| 898 |
urbaneks |
2232 |
textFont = [newFont retain];
|
|
|
2233 |
}
|
| 1603 |
urbaneks |
2234 |
|
|
|
2235 |
argsHints=[Preferences flagForKey:prefShowArgsHints withDefault:YES];
|
|
|
2236 |
|
| 815 |
urbaneks |
2237 |
{
|
|
|
2238 |
int i = 0, ccs = [consoleColorsKeys count];
|
|
|
2239 |
while (i<ccs) {
|
|
|
2240 |
NSColor *c = [Preferences unarchivedObjectForKey: [consoleColorsKeys objectAtIndex:i] withDefault: [consoleColors objectAtIndex:i]];
|
|
|
2241 |
if (c != [consoleColors objectAtIndex:i]) {
|
|
|
2242 |
[consoleColors replaceObjectAtIndex:i withObject:c];
|
|
|
2243 |
if (i == iBackgroundColor) {
|
|
|
2244 |
[RConsoleWindow setBackgroundColor:c];
|
|
|
2245 |
[RConsoleWindow display];
|
|
|
2246 |
}
|
|
|
2247 |
}
|
|
|
2248 |
i++;
|
|
|
2249 |
}
|
| 1489 |
urbaneks |
2250 |
[RTextView setInsertionPointColor:[consoleColors objectAtIndex:iInputColor]];
|
| 815 |
urbaneks |
2251 |
}
|
|
|
2252 |
[RTextView setNeedsDisplay:YES];
|
| 1328 |
urbaneks |
2253 |
SLog(@" - done, preferences updated");
|
| 568 |
iacus |
2254 |
}
|
|
|
2255 |
|
| 1490 |
urbaneks |
2256 |
- (NSWindow*) window {
|
|
|
2257 |
return RConsoleWindow;
|
|
|
2258 |
}
|
|
|
2259 |
|
| 638 |
iacus |
2260 |
- (NSTextView *)getRTextView{
|
|
|
2261 |
return RTextView;
|
|
|
2262 |
}
|
|
|
2263 |
|
|
|
2264 |
- (NSWindow *)getRConsoleWindow{
|
|
|
2265 |
return RConsoleWindow;
|
|
|
2266 |
}
|
|
|
2267 |
|
| 1488 |
urbaneks |
2268 |
- (void) setStatusLineText: (NSString*) text {
|
|
|
2269 |
SLog(@"RController.setStatusLine: \"%@\"", text);
|
|
|
2270 |
[statusLine setStringValue:text?text:@""];
|
|
|
2271 |
}
|
|
|
2272 |
|
| 1491 |
urbaneks |
2273 |
- (NSString*) statusLineText {
|
|
|
2274 |
return [statusLine stringValue];
|
|
|
2275 |
}
|
|
|
2276 |
|
| 568 |
iacus |
2277 |
@end
|
|
|
2278 |
|
|
|
2279 |
|