1 | //
|
---|
2 | // FSAController.m
|
---|
3 | // F-Script Anywhere
|
---|
4 | //
|
---|
5 | // Created by Nicholas Riley on Fri Feb 01 2002.
|
---|
6 | // Copyright (c) 2002 Nicholas Riley. All rights reserved.
|
---|
7 | //
|
---|
8 |
|
---|
9 | /*
|
---|
10 |
|
---|
11 | F-Script Anywhere is free software; you can redistribute it and/or modify
|
---|
12 | it under the terms of the GNU General Public License as published by
|
---|
13 | the Free Software Foundation; either version 2 of the License, or
|
---|
14 | (at your option) any later version.
|
---|
15 |
|
---|
16 | F-Script Anywhere is distributed in the hope that it will be useful,
|
---|
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
19 | GNU General Public License for more details.
|
---|
20 |
|
---|
21 | You should have received a copy of the GNU General Public License
|
---|
22 | along with F-Script Anywhere; if not, write to the Free Software
|
---|
23 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
24 |
|
---|
25 | */
|
---|
26 |
|
---|
27 | #import "FSAController.h"
|
---|
28 | #import "FSAViewAssociationController.h"
|
---|
29 | #import "FSAnywhere.h"
|
---|
30 | #import "FSAWindowManager.h"
|
---|
31 | #import <FScript/FSInterpreter.h>
|
---|
32 |
|
---|
33 | @class ShellView;
|
---|
34 |
|
---|
35 | // XXX workaround for lack of focus on FSInterpreterView
|
---|
36 | @interface CLIView : NSView
|
---|
37 | - (ShellView *)shellView;
|
---|
38 | @end
|
---|
39 |
|
---|
40 | @interface FSInterpreterView (FSAWorkaround)
|
---|
41 | - (CLIView *)cliView;
|
---|
42 | @end
|
---|
43 |
|
---|
44 | @interface NSWindow (RoundedCorners)
|
---|
45 | - (void)setBottomCornerRounded:(BOOL)yn;
|
---|
46 | @end
|
---|
47 |
|
---|
48 | @implementation FSAController
|
---|
49 |
|
---|
50 | // derived from TETextWatcher.m in Mike Ferris's TextExtras
|
---|
51 | + (void)installMenu;
|
---|
52 | {
|
---|
53 | static BOOL alreadyInstalled = NO;
|
---|
54 | NSMenu *mainMenu = nil;
|
---|
55 |
|
---|
56 | if (!alreadyInstalled && ((mainMenu = [NSApp mainMenu]) != nil)) {
|
---|
57 | NSMenu *insertIntoMenu = nil;
|
---|
58 | NSMenuItem *item;
|
---|
59 | unsigned insertLoc = NSNotFound;
|
---|
60 | NSBundle *bundle = [NSBundle bundleForClass: self];
|
---|
61 | NSMenu * beforeSubmenu = [NSApp windowsMenu];
|
---|
62 | // Succeed or fail, we do not try again.
|
---|
63 | alreadyInstalled = YES;
|
---|
64 |
|
---|
65 | // Add it to the main menu. We try to put it right before the Windows menu if there is one, or right before the Services menu if there is one, and if there's neither we put it right before the the last submenu item (ie above Quit and Hide on Mach, at the end on Windows.)
|
---|
66 |
|
---|
67 | if (!beforeSubmenu) {
|
---|
68 | beforeSubmenu = [NSApp servicesMenu];
|
---|
69 | }
|
---|
70 |
|
---|
71 | insertIntoMenu = mainMenu;
|
---|
72 |
|
---|
73 | if (beforeSubmenu) {
|
---|
74 | NSArray *itemArray = [insertIntoMenu itemArray];
|
---|
75 | unsigned i, c = [itemArray count];
|
---|
76 |
|
---|
77 | // Default to end of menu
|
---|
78 | insertLoc = c;
|
---|
79 |
|
---|
80 | for (i=0; i<c; i++) {
|
---|
81 | if ([[itemArray objectAtIndex:i] target] == beforeSubmenu) {
|
---|
82 | insertLoc = i;
|
---|
83 | break;
|
---|
84 | }
|
---|
85 | }
|
---|
86 | } else {
|
---|
87 | NSArray *itemArray = [insertIntoMenu itemArray];
|
---|
88 | unsigned i = [itemArray count];
|
---|
89 |
|
---|
90 | // Default to end of menu
|
---|
91 | insertLoc = i;
|
---|
92 |
|
---|
93 | while (i-- > 0) {
|
---|
94 | if ([[itemArray objectAtIndex:i] hasSubmenu]) {
|
---|
95 | insertLoc = i+1;
|
---|
96 | break;
|
---|
97 | }
|
---|
98 | }
|
---|
99 | }
|
---|
100 | if (insertIntoMenu) {
|
---|
101 | NSMenu *fsaMenu = [[NSMenu allocWithZone: [NSMenu menuZone]] initWithTitle:NSLocalizedStringFromTableInBundle(@"FSA", @"FSA", bundle, @"Title of F-Script Anywhere menu")];
|
---|
102 |
|
---|
103 | item = [insertIntoMenu insertItemWithTitle: NSLocalizedStringFromTableInBundle(@"FSA", @"FSA", bundle, @"Title of F-Script Anywhere menu") action:NULL keyEquivalent:@"" atIndex:insertLoc];
|
---|
104 | [insertIntoMenu setSubmenu:fsaMenu forItem:item];
|
---|
105 | [fsaMenu release];
|
---|
106 |
|
---|
107 | // Add the items for the commands.
|
---|
108 | item = [fsaMenu addItemWithTitle: NSLocalizedStringFromTableInBundle(@"New F-Script Workspace", @"FSA", bundle, @"Title of F-Script Workspace menu item") action:@selector(createInterpreterWindow:) keyEquivalent: @""];
|
---|
109 | [item setTarget: self];
|
---|
110 | item = [fsaMenu addItemWithTitle: NSLocalizedStringFromTableInBundle(@"Browser for Target...", @"FSA", bundle, @"Title of F-Script Workspace menu item") action:@selector(createBrowserForSelection:) keyEquivalent: @""];
|
---|
111 | [item setTarget: self];
|
---|
112 | [fsaMenu addItem: [NSMenuItem separatorItem]];
|
---|
113 | item = [fsaMenu addItemWithTitle: NSLocalizedStringFromTableInBundle(@"About F-Script Anywhere", @"FSA", bundle, @"Title of Info Panel menu item") action:@selector(showInfo:) keyEquivalent: @""];
|
---|
114 | [item setTarget: self];
|
---|
115 | [[FSAWindowManager sharedManager] setWindowMenu: fsaMenu];
|
---|
116 | }
|
---|
117 | }
|
---|
118 |
|
---|
119 | }
|
---|
120 |
|
---|
121 | + (BOOL)validateMenuItem:(NSMenuItem *)menuItem
|
---|
122 | {
|
---|
123 | SEL sel;
|
---|
124 | NSAssert([menuItem target] == self, @"menu item does not target FSAController!");
|
---|
125 | sel = [menuItem action];
|
---|
126 | if (sel == @selector(showInfo:) || sel == @selector(createInterpreterWindow:) || sel == @selector(createBrowserForSelection:)) return YES;
|
---|
127 | FSALog(@"+[FSAController validateMenuItem:] unknown menu item for validation: %@", menuItem);
|
---|
128 | return NO;
|
---|
129 | }
|
---|
130 |
|
---|
131 | + (void)createInterpreterWindow:(id)sender;
|
---|
132 | {
|
---|
133 | [[self alloc] initShowingInterpreter:YES];
|
---|
134 | }
|
---|
135 |
|
---|
136 | + (void)createBrowserForSelection:(id)sender;
|
---|
137 | {
|
---|
138 | [[[self alloc] initShowingInterpreter:NO] FSA_associateAndOpenBrowser:sender];
|
---|
139 | }
|
---|
140 |
|
---|
141 | + (void)showInfo:(id)sender;
|
---|
142 | {
|
---|
143 | int result = NSRunInformationalAlertPanel([NSString stringWithFormat: @"About F-Script Anywhere (version %s)", FSA_VERSION], @"F-Script Anywhere lets you embed a F-Script interpreter in a Cocoa application while it is running.\n\nF-Script Anywhere is currently installed in this application. To remove it, quit this application.\n\nFor more information about F-Script, please visit its Web site %@.", @"OK", @"Visit Web Site", nil, FSA_FScriptURL);
|
---|
144 | if (result == NSAlertAlternateReturn) {
|
---|
145 | [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: FSA_FScriptURL]];
|
---|
146 | }
|
---|
147 | }
|
---|
148 |
|
---|
149 | - (id)initShowingInterpreter:(BOOL)showInterpreter {
|
---|
150 | self = [super initWithWindowNibName: @"FSAInterpreterPanel"];
|
---|
151 |
|
---|
152 | if (self != nil) {
|
---|
153 | NSWindow *window = [self window];
|
---|
154 | NSString *label;
|
---|
155 | static unsigned numInterpWindows = 0;
|
---|
156 |
|
---|
157 | if (showInterpreter) {
|
---|
158 | NSAssert(window != nil, @"Can't get interpreter window!");
|
---|
159 | if ([window respondsToSelector: @selector(setBottomCornerRounded:)])
|
---|
160 | [window setBottomCornerRounded: NO]; // XXX otherwise we get black areas because the window isn't rounded
|
---|
161 | [window setContentBorderThickness: 30 forEdge: NSMinYEdge];
|
---|
162 | // XXX why oh why is text not vertically centered in standard-sized buttons?
|
---|
163 | [openBrowserButton setFrameSize: NSMakeSize([openBrowserButton frame].size.width, 19)];
|
---|
164 | [associateWithInterfaceButton setFrameSize: NSMakeSize([associateWithInterfaceButton frame].size.width, 19)];
|
---|
165 | [floatButtonCell setBackgroundStyle: NSBackgroundStyleRaised];
|
---|
166 | if (interpreterNum == 0) interpreterNum = ++numInterpWindows;
|
---|
167 | if ( (label = [self interpreterLabel]) != nil) {
|
---|
168 | [window setTitle: [NSString stringWithFormat: @"%@: %@", [window title], label]];
|
---|
169 | }
|
---|
170 |
|
---|
171 | [window setLevel: NSNormalWindowLevel]; // XXX if set floating, it is globally floating!
|
---|
172 | [self showWindow: self];
|
---|
173 | [window makeKeyAndOrderFront: self];
|
---|
174 | #warning this should go away when F-Script properly accepts firstResponder on the InterpreterView
|
---|
175 | [window makeFirstResponder: (NSView *)[[[self interpreterView] cliView] shellView]];
|
---|
176 | [[FSAWindowManager sharedManager] registerWindow: window];
|
---|
177 | }
|
---|
178 | system = [[[self interpreterView] interpreter] objectForIdentifier: @"sys" found: NULL];
|
---|
179 | [system retain];
|
---|
180 | NSAssert1([system isKindOfClass: [System class]], @"Initial value bound to identifier 'sys' is not a System object, but %@", system);
|
---|
181 | }
|
---|
182 |
|
---|
183 | return self;
|
---|
184 | }
|
---|
185 |
|
---|
186 | - (void)dealloc;
|
---|
187 | {
|
---|
188 | [system release];
|
---|
189 | [super dealloc];
|
---|
190 | }
|
---|
191 |
|
---|
192 | - (NSString *)interpreterLabel;
|
---|
193 | {
|
---|
194 | static NSString *appName = nil;
|
---|
195 | static BOOL retrievedAppName = NO;
|
---|
196 |
|
---|
197 | if (appName == nil) {
|
---|
198 | if (retrievedAppName) return nil;
|
---|
199 | NS_DURING
|
---|
200 | appName = [[[NSBundle mainBundle] infoDictionary] objectForKey: @"CFBundleName"];
|
---|
201 | NS_HANDLER
|
---|
202 | FSALog(@"Exception occurred while trying to obtain application name: %@", localException);
|
---|
203 | NS_ENDHANDLER
|
---|
204 | retrievedAppName = YES;
|
---|
205 | }
|
---|
206 | if (interpreterNum == 1) return appName;
|
---|
207 | return [NSString stringWithFormat: @"%@ [%u]", appName, interpreterNum];
|
---|
208 | }
|
---|
209 |
|
---|
210 | - (FSInterpreterView *)interpreterView;
|
---|
211 | {
|
---|
212 | return interpreterView;
|
---|
213 | }
|
---|
214 |
|
---|
215 | - (System *)system;
|
---|
216 | {
|
---|
217 | return system;
|
---|
218 | }
|
---|
219 |
|
---|
220 | - (IBAction)setFloating:(id)sender;
|
---|
221 | {
|
---|
222 | [[self window] setLevel: [sender state] == NSOnState ? NSFloatingWindowLevel : NSNormalWindowLevel];
|
---|
223 | }
|
---|
224 |
|
---|
225 | - (IBAction)FSA_associateAndOpenBrowser:(id)sender
|
---|
226 | {
|
---|
227 | if (viewAssociationController == nil) {
|
---|
228 | viewAssociationController = [[FSAViewAssociationController alloc] initWithFSAController: self];
|
---|
229 | }
|
---|
230 | [viewAssociationController captureOneView];
|
---|
231 | }
|
---|
232 |
|
---|
233 | @end
|
---|