-
Notifications
You must be signed in to change notification settings - Fork 2
/
ScriptWindowController.m
373 lines (317 loc) · 15.3 KB
/
ScriptWindowController.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#import "ScriptWindowController.h"
#import "ScriptBrowserDocument.h"
#import "ScriptEditorDocument.h"
#import "ScriptDocument+Running.h"
#import "ScriptReference.h"
#import "KwikiController.h"
#import "ScriptPropertyEditingController.h"
#import "DataView.h"
#import "UserDefaults.h"
#import "VariablesTableController.h"
#import "ScriptVariable.h"
#import "SupportFilesWindowController.h"
#import <JaguarFoundation/JaguarFoundation.h>
#import <JaguarAppKit/JaguarAppKit.h>
@implementation ScriptWindowController
static NSString * const ScriptWindowToolbarIdentifier = @"ScriptWindowToolbarIdentifier";
static NSString * const ScriptWindowToggleErrorsDrawerItemIdentifier = @"ScriptWindowToggleErrorsDrawerItemIdentifier";
#pragma mark Init and Cleanup
- (id)init
{
if ((self = [super initWithWindowNibName:@"ScriptWindow"])) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (NSString *)toolbarIdentifier
{
return ScriptWindowToolbarIdentifier;
}
- (void)windowDidLoad
{
id newView;
NSPopUpButton *cornerButton;
NSMenu *variablesMenu, *variableTypesMenu;
NSMenuItem *menuItem;
float width, height;
[errorsTextView setRichText:NO];
[errorsTextView setUsesFontPanel:NO];
[errorsTextView setUsesRuler:NO];
[errorsTextView setUsesFindPanel:YES];
// Configure the input text view
newView = [JGExtendedTextView fullyConfiguredTextView];
[newView setFrame:[scriptTextView frame]];
[[scriptTextView superview] replaceSubview:scriptTextView with:newView];
scriptTextView = [newView documentView];
[scriptTextView setDelegate:self];
[scriptTextView setRichText:NO];
[scriptTextView setUsesFontPanel:NO];
[scriptTextView setUsesFindPanel:YES];
[scriptTextView setUsesRuler:NO];
[scriptTextView setAllowsUndo:YES];
[scriptTextView bind:@"lineNumbersVisible" toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[NSString stringWithFormat:@"values.%@", PipeLineNumbersDefaultKey] options:nil];
[scriptTextView bind:@"font" toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[NSString stringWithFormat:@"values.%@", PipeFontDefaultKey] options:[NSDictionary dictionaryWithObject:NSUnarchiveFromDataTransformerName forKey:@"NSValueTransformerName"]];
[scriptTextView bind:@"value" toObject:documentController withKeyPath:@"selection.script" options:nil];
[scriptTextView bind:@"backgroundColor" toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[NSString stringWithFormat:@"values.%@", PipeScriptBackgroundColorDefaultKey] options:[NSDictionary dictionaryWithObject:NSUnarchiveFromDataTransformerName forKey:@"NSValueTransformerName"]];
[scriptTextView bind:@"autoIndents" toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[NSString stringWithFormat:@"values.%@", PipeAutoIndentDefaultKey] options:nil];
[scriptTextView bind:@"softWraps" toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[NSString stringWithFormat:@"values.%@", PipeSoftWrapDefaultKey] options:nil];
[[scriptTextView enclosingScrollView] bind:@"verticalLineScroll" toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath:[NSString stringWithFormat:@"values.%@", PipeFontDefaultKey] options:[NSDictionary dictionaryWithObject:JGFontLineHeightTransformerName forKey:@"NSValueTransformerName"]];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewDidUpdateFromExternalEditor:) name:JGExtendedTextViewDidUpdateFromExternalEditorNotification object:scriptTextView];
[variablesTableController setEditable:YES];
[super windowDidLoad];
variableTypesMenu = [[NSMenu alloc] initWithTitle:@"Variable Types"];
menuItem = [[NSMenuItem alloc] init];
[menuItem setTitle:NSLocalizedString(@"Text", @"Variable type")];
[menuItem setAction:@selector(switchVariableType:)];
[menuItem setTarget:variablesTableController];
[menuItem setTag:ScriptTextVariableType];
[variableTypesMenu addItem:menuItem];
[menuItem release];
menuItem = [[NSMenuItem alloc] init];
[menuItem setTitle:NSLocalizedString(@"Password", @"Variable type")];
[menuItem setAction:@selector(switchVariableType:)];
[menuItem setTarget:variablesTableController];
[menuItem setTag:ScriptPasswordVariableType];
[variableTypesMenu addItem:menuItem];
[menuItem release];
menuItem = [[NSMenuItem alloc] init];
[menuItem setTitle:NSLocalizedString(@"Boolean", @"Variable type")];
[menuItem setAction:@selector(switchVariableType:)];
[menuItem setTarget:variablesTableController];
[menuItem setTag:ScriptBooleanVariableType];
[variableTypesMenu addItem:menuItem];
[menuItem release];
menuItem = [[NSMenuItem alloc] init];
[menuItem setTitle:NSLocalizedString(@"Enumeration", @"Variable type")];
[menuItem setAction:@selector(switchVariableType:)];
[menuItem setTarget:variablesTableController];
[menuItem setTag:ScriptEnumVariableType];
[variableTypesMenu addItem:menuItem];
[menuItem release];
[variableTypesMenu autorelease];
variablesMenu = [[NSMenu alloc] initWithTitle:@""];
[variablesMenu addItemWithTitle:@"" action:NULL keyEquivalent:@""];
[[variablesMenu addItemWithTitle:NSLocalizedString(@"Add Variable", @"Menu item for adding a new variable") action:@selector(add:) keyEquivalent:@""] setTarget:variablesTableController];
[[variablesMenu addItemWithTitle:NSLocalizedString(@"Remove Variable", @"Menu item for deleting a variable") action:@selector(remove:) keyEquivalent:@""] setTarget:variablesTableController];
[variablesMenu addItem:[NSMenuItem separatorItem]];
[[variablesMenu addItemWithTitle:NSLocalizedString(@"Variable Type", @"Item in variables menu") action:NULL keyEquivalent:@""] setSubmenu:variableTypesMenu];
[[variablesMenu addItemWithTitle:NSLocalizedString(@"Enumeration Values...", @"Menu item for configuring enumeration values of a variable") action:@selector(configureEnumValues:) keyEquivalent:@""] setTarget:variablesTableController];
[variablesMenu autorelease];
cornerButton = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:YES];
width = NSWidth([[[variablesTableView enclosingScrollView] verticalScroller] frame]);
height = NSHeight([[variablesTableView headerView] frame]);
[cornerButton setFrameSize:NSMakeSize(width, height)];
[cornerButton setBezelStyle:NSShadowlessSquareBezelStyle];
[cornerButton setMenu:variablesMenu];
[variablesTableView setCornerView:cornerButton];
[cornerButton release];
}
#pragma mark Accessors
- (void)setDocument:(NSDocument *)newDocument
{
if (newDocument != [self document]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:[self document]];
[super setDocument:newDocument];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(performErrorNotification:) name:ScriptDocumentErrorNotification object:newDocument];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scriptDocumentWillRun:) name:ScriptDocumentWillRunNotification object:newDocument];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scriptDocumentWillSave:) name:ScriptDocumentWillSaveNotification object:newDocument];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scriptDocumentWillRevert:) name:ScriptDocumentWillRevertNotification object:newDocument];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scriptDocumentCollectSavedPropertyList:) name:ScriptEditorDocumentCollectSavedPropertyListNotification object:newDocument];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scriptDocumentApplySavedPropertyList:) name:ScriptEditorDocumentApplySavedPropertyListNotification object:newDocument];
}
}
- (NSDictionary *)savedPropertyList
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[[self window] stringWithSavedFrame], @"savedFrame",
[NSNumber numberWithFloat:[horizontalSplitView subviewRatio]], @"horizontalSplitRatio",
[NSNumber numberWithFloat:[verticalSplitView subviewRatio]], @"verticalSplitRatio",
[NSNumber numberWithBool:([infoDrawer state] == NSDrawerOpenState)], @"infoDrawerOpen",
NSStringFromRange([scriptTextView selectedRange]), @"selectedScriptRange",
NSStringFromRange([inputTextView selectedRange]), @"selectedInputRange",
nil];
}
- (void)applySavedPropertyList:(NSDictionary *)propertyList
{
id property;
if (propertyList != nil) {
if ((property = [propertyList objectForKey:@"savedFrame"])) {
// Do not restore the saved frame if this is a new document (e.g. based on a template)
if ([[self document] fileName] != nil)
[[self window] setFrameFromString:property];
}
if ((property = [propertyList objectForKey:@"horizontalSplitRatio"]))
[horizontalSplitView adjustSubviewsWithRatio:[property floatValue]];
if ((property = [propertyList objectForKey:@"verticalSplitRatio"]))
[verticalSplitView adjustSubviewsWithRatio:[property floatValue]];
if ((property = [propertyList objectForKey:@"selectedScriptRange"])) {
[scriptTextView setSelectedRange:NSRangeFromString(property)];
[scriptTextView scrollRangeToVisible:NSRangeFromString(property)];
}
if ((property = [propertyList objectForKey:@"selectedInputRange"])) {
[inputTextView setSelectedRange:NSRangeFromString(property)];
[inputTextView scrollRangeToVisible:NSRangeFromString(property)];
}
if ((property = [propertyList objectForKey:@"infoDrawerOpen"])) {
if ([property boolValue])
[infoDrawer open];
}
}
}
#pragma mark Actions
- (IBAction)toggleErrorsDrawer:(id)sender
{
[errorsDrawer toggle:self];
}
- (IBAction)addToScriptBrowser:(id)sender
{
ScriptReference *newReference;
if ([[KwikiController sharedController] indexOfKwikiWithPath:[[self document] fileName]] == NSNotFound) {
newReference = [[ScriptReference alloc] initWithPath:[[self document] fileName]];
[[KwikiController sharedController] insertObject:newReference inKwikisAtIndex:[[KwikiController sharedController] countOfKwikis]];
[newReference release];
}
else NSBeep();
}
- (IBAction)focusScript:(id)sender
{
[[self window] makeFirstResponder:scriptTextView];
}
- (IBAction)editScriptSettings:(id)sender
{
if ([[self window] makeFirstResponder:nil])
[propertyEditingController beginSheet];
}
- (IBAction)editSupportFiles:(id)sender
{
SupportFilesWindowController *windowController;
windowController = [[SupportFilesWindowController alloc] init];
[windowController setSupportFiles:[[self document] valueForKey:@"supportFiles"]];
[windowController beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(editSupportFilesDidEnd:returnCode:contextInfo:) contextInfo:NULL];
[windowController release];
}
- (void)editSupportFilesDidEnd:(SupportFilesWindowController *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
{
if (returnCode == NSOKButton) {
[[[self document] mutableArrayValueForKey:@"supportFiles"] setArray:[sheet supportFiles]];
}
}
#pragma mark User Interface Validation
- (BOOL)validateMenuItem:(NSMenuItem *)anItem
{
SEL action = [anItem action];
unsigned scriptIndex;
if (action == @selector(addToScriptBrowser:)) {
[anItem setTitle:NSLocalizedString(@"Add to Script Browser", @"Menu item for adding the current script to the script browser")];
if ([[self document] fileName] && [[self document] fileType]) {
scriptIndex = [[KwikiController sharedController] indexOfKwikiWithPath:[[self document] fileName]];
return scriptIndex == NSNotFound;
}
else
return NO;
}
else if (action == @selector(toggleErrorsDrawer:)) {
if ([errorsDrawer state] == NSDrawerOpenState)
[anItem setTitle:NSLocalizedString(@"Hide Errors", @"Menu item for hiding the errors drawer")];
else
[anItem setTitle:NSLocalizedString(@"Show Errors", @"Menu item for showing the errors drawer")];
return YES;
}
else if (action == @selector(saveScriptSourceToFile:)) {
if ([anItem tag] == 1)
return [[[self document] scriptSourceAlias] path] != nil;
else
return YES;
}
else return [self validateUserInterfaceItem:anItem];
}
#pragma mark Notifications
- (void)scriptDocumentApplySavedPropertyList:(NSNotification *)aNotification
{
NSDictionary *propertyList;
propertyList = [[aNotification userInfo] objectForKey:@"propertyList"];
[self applySavedPropertyList:[propertyList objectForKey:PipeScriptEditorWindowPropertiesPlistKey]];
}
- (void)scriptDocumentCollectSavedPropertyList:(NSNotification *)aNotification
{
NSMutableDictionary *propertyList;
propertyList = [[aNotification userInfo] objectForKey:@"propertyList"];
[propertyList setObject:[self savedPropertyList] forKey:PipeScriptEditorWindowPropertiesPlistKey];
}
- (void)scriptDocumentWillRun:(NSNotification *)aNotification
{
[super scriptDocumentWillRun:aNotification];
// workaround to prevent strange drawing glitch (AppKit bug?)
[[errorsDrawer contentView] setNeedsDisplay:YES];
}
- (void)scriptDocumentWillSave:(NSNotification *)aNotification
{
[self commitAllEditing];
}
- (void)scriptDocumentWillRevert:(NSNotification *)aNotification
{
[self discardAllEditing];
}
- (void)performErrorNotification:(NSNotification *)aNotification
{
[errorsDrawer open];
}
#pragma mark Toolbar
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
{
return [[super toolbarAllowedItemIdentifiers:toolbar] arrayByAddingObjectsFromArray:[NSArray arrayWithObjects:
ScriptWindowToggleErrorsDrawerItemIdentifier,
nil]];
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
{
NSToolbarItem *item;
item = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
if ([itemIdentifier isEqualToString:ScriptWindowToggleErrorsDrawerItemIdentifier]) {
[item setLabel:NSLocalizedString(@"Errors", @"Toolbar item for toggling the errors drawer")];
[item setImage:[NSImage imageNamed:@"Errors Button"]];
[item setAction:@selector(toggleErrorsDrawer:)];
[item setTarget:nil];
}
else return [super toolbar:toolbar itemForItemIdentifier:itemIdentifier willBeInsertedIntoToolbar:flag];
if ([item paletteLabel] == nil || [[item paletteLabel] isEqualToString:@""])
[item setPaletteLabel:[item label]];
return item;
}
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
{
return [NSArray arrayWithObjects:
ScriptRunToolbarItemIdentifier,
ScriptStopToolbarItemIdentifier,
ScriptReverseTransformationToolbarItemIdentifier,
NSToolbarSeparatorItemIdentifier,
ScriptOutputToInputToolbarItemIdentifier,
NSToolbarFlexibleSpaceItemIdentifier,
ScriptWindowToggleErrorsDrawerItemIdentifier,
ScriptToggleInfoToolbarItemIdentifier,
nil];
}
#pragma mark Text View Delegate
- (NSString *)fileExtensionForExternalEditorOfTextView:(JGExtendedTextView *)textView
{
if (textView == scriptTextView)
return [[[self document] preferredScriptFileName] pathExtension];
else
return nil;
}
- (NSString *)customFilenameForExternalEditorOfTextView:(JGExtendedTextView *)textView
{
NSMutableString *customFilename = [NSMutableString string];
[customFilename setString:NSLocalizedString(@"Pipe: ", @"Prefix for external editor display name")];
[customFilename appendString:[[self document] displayName]];
[customFilename appendString:@": "];
if (textView == scriptTextView)
[customFilename appendString:NSLocalizedString(@"Script Source", @"External editor display name for script source")];
else if (textView == inputTextView)
[customFilename appendString:NSLocalizedString(@"Input", @"External editor display name for input")];
return customFilename;
}
@end