ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/MacOSX/Launcher/VMListController.mm
Revision: 1.11
Committed: 2010-12-28T02:57:46Z (13 years, 10 months ago) by asvitkine
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +6 -0 lines
Log Message:
abort editing virtual machine name if any action is chosen

File Contents

# Content
1 /*
2 * VMListController.mm - SheepShaver VM manager in Cocoa on Mac OS X
3 *
4 * Copyright (C) 2009 Alexei Svitkine
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #import "VMListController.h"
22 #import "VMSettingsController.h"
23
24 /*
25
26 TODO:
27
28 Drag VM from Finder to import
29 When choosing things like rom file and keycode files - have a checkbox to copy
30 selected file into the bundle.
31 Copy path!
32
33 */
34
35 @interface NSObject (TableViewContextMenu)
36 - (NSMenu *) tableView: (NSTableView *) tableView menuForEvent: (NSEvent *) event;
37 @end
38
39 @implementation NSTableView (ContextMenu)
40 - (NSMenu *) menuForEvent: (NSEvent *) event
41 {
42 if ([[self delegate] respondsToSelector:@selector(tableView:menuForEvent:)])
43 return [[self delegate] tableView:self menuForEvent:event];
44 return nil;
45 }
46 @end
47
48 #define VM_DRAG_TYPE @"sheepvm"
49
50 @implementation VMListController
51
52 + (id) sharedInstance
53 {
54 static VMListController *_sharedInstance = nil;
55 if (!_sharedInstance) {
56 _sharedInstance = [[VMListController allocWithZone:[self zone]] init];
57 }
58 return _sharedInstance;
59 }
60
61 - (id) init
62 {
63 self = [super initWithWindowNibName:@"VMListWindow"];
64
65 NSArray *vms = [[NSUserDefaults standardUserDefaults] stringArrayForKey:@"vm_list"];
66 vmArray = [[NSMutableArray alloc] initWithCapacity:[vms count]];
67 [vmArray addObjectsFromArray:vms];
68
69 tasks = [[NSMutableDictionary alloc] init];
70
71 [[NSNotificationCenter defaultCenter] addObserver:self
72 selector:@selector(onTaskTerminated:)
73 name:NSTaskDidTerminateNotification
74 object:nil];
75
76 return self;
77 }
78
79 - (void) awakeFromNib
80 {
81 [[[vmList tableColumns] objectAtIndex:0] setEditable:YES];
82 [vmList setDataSource: self];
83 [vmList setDelegate: self];
84 [vmList reloadData];
85 [vmList registerForDraggedTypes:[NSArray arrayWithObjects:VM_DRAG_TYPE, nil]];
86 }
87
88 - (void) _showNotFoundAlert
89 {
90 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
91 [alert setMessageText:@"The virtual machine cannot be found."];
92 [alert setAlertStyle:NSWarningAlertStyle];
93 [alert beginSheetModalForWindow:[self window]
94 modalDelegate:self
95 didEndSelector:nil
96 contextInfo:nil];
97 }
98
99 - (void) reloadDataAndSave
100 {
101 [vmList reloadData];
102 [[NSUserDefaults standardUserDefaults] setObject:vmArray forKey:@"vm_list"];
103 }
104
105 - (void) keyDown: (NSEvent *) event
106 {
107 if ([event type] == NSKeyDown && [[event characters] length] > 0) {
108 unichar key = [[event characters] characterAtIndex:0];
109 if (key == NSDeleteFunctionKey || key == NSDeleteCharacter) {
110 [self deleteVirtualMachine:self];
111 }
112 }
113 }
114
115 - (int) numberOfRowsInTableView: (NSTableView *) table
116 {
117 return [vmArray count];
118 }
119
120 - (id) tableView: (NSTableView *) table objectValueForTableColumn: (NSTableColumn *) c row: (int) r
121 {
122 return [[[vmArray objectAtIndex: r] lastPathComponent] stringByDeletingPathExtension];
123 }
124
125 - (void) tableView: (NSTableView *) table setObjectValue: (id) value forTableColumn: (NSTableColumn *) c row: (int) r
126 {
127 NSString *currentPath = [vmArray objectAtIndex: r];
128 NSString *newPath = [[NSString stringWithFormat:@"%@/%@.sheepvm",
129 [currentPath stringByDeletingLastPathComponent], value] retain];
130 if (![currentPath isEqual:newPath]) {
131 if ([[NSFileManager defaultManager] fileExistsAtPath:currentPath]) {
132 NSFileManager *manager = [NSFileManager defaultManager];
133 if ([manager movePath: currentPath toPath: newPath handler:nil]) {
134 [vmArray replaceObjectAtIndex: r withObject: newPath];
135 [currentPath release];
136 }
137 } else {
138 [self _showNotFoundAlert];
139 }
140 }
141 }
142
143 - (void) tableViewSelectionDidChange: (NSNotification *) notification
144 {
145 if ([vmList selectedRow] >= 0) {
146 [settingsButton setEnabled:YES];
147 [launchButton setEnabled:YES];
148 } else {
149 [settingsButton setEnabled:NO];
150 [launchButton setEnabled:NO];
151 }
152 }
153
154 - (BOOL) tableView: (NSTableView *) table writeRowsWithIndexes: (NSIndexSet *) rows toPasteboard: (NSPasteboard *) pboard
155 {
156 vmBeingDragged = [vmArray objectAtIndex:[rows firstIndex]];
157 [pboard declareTypes:[NSArray arrayWithObject:VM_DRAG_TYPE] owner:self];
158 [pboard setString:VM_DRAG_TYPE forType:VM_DRAG_TYPE];
159 return YES;
160 }
161
162 - (NSDragOperation) tableView: (NSTableView *) table validateDrop: (id <NSDraggingInfo>) info proposedRow: (int) row proposedDropOperation: (NSTableViewDropOperation) op
163 {
164 if (op == NSTableViewDropAbove && row != -1) {
165 return NSDragOperationPrivate;
166 } else {
167 return NSDragOperationNone;
168 }
169 }
170
171 - (BOOL) tableView: (NSTableView *) table acceptDrop: (id <NSDraggingInfo>) info row: (int) row dropOperation: (NSTableViewDropOperation) op
172 {
173 if ([[[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject:VM_DRAG_TYPE]] isEqualToString:VM_DRAG_TYPE]) {
174 [vmList deselectAll:nil];
175 int index = [vmArray indexOfObject:vmBeingDragged];
176 if (index != row) {
177 [vmArray insertObject:vmBeingDragged atIndex:row];
178 if (row <= index) {
179 index += 1;
180 } else {
181 row -= 1;
182 }
183 [vmArray removeObjectAtIndex: index];
184 }
185 [self reloadDataAndSave];
186 [vmList selectRow:row byExtendingSelection:NO];
187 return YES;
188 }
189
190 return NO;
191 }
192
193 - (NSMenu *) tableView: (NSTableView *) table menuForEvent: (NSEvent *) event
194 {
195 NSMenu *menu = nil;
196 int row = [table rowAtPoint:[table convertPoint:[event locationInWindow] fromView:nil]];
197 if (row >= 0) {
198 [table selectRow:row byExtendingSelection:NO];
199 menu = [[[NSMenu alloc] initWithTitle: @"Contextual Menu"] autorelease];
200 [menu addItemWithTitle: @"Launch Virtual Machine"
201 action: @selector(launchVirtualMachine:) keyEquivalent: @""];
202 [menu addItemWithTitle: @"Edit VM Settings..."
203 action: @selector(editVirtualMachineSettings:) keyEquivalent: @""];
204 [menu addItemWithTitle: @"Reveal VM in Finder"
205 action: @selector(revealVirtualMachineInFinder:) keyEquivalent: @""];
206 [menu addItemWithTitle: @"Remove VM from List"
207 action: @selector(deleteVirtualMachine:) keyEquivalent: @""];
208 }
209 return menu;
210 }
211
212 - (NSString *) tableView: (NSTableView *) table toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
213 tableColumn: (NSTableColumn *) c row: (int) r mouseLocation: (NSPoint) loc
214 {
215 return [vmArray objectAtIndex: r];
216 }
217
218 - (IBAction) newVirtualMachine: (id) sender
219 {
220 [vmList abortEditing];
221 NSSavePanel *save = [NSSavePanel savePanel];
222 [save setMessage: @"New SheepShaver Virtual Machine:"];
223 [save setRequiredFileType: @"sheepvm"];
224 [save setCanSelectHiddenExtension: YES];
225 [save setExtensionHidden: NO];
226 [save beginSheetForDirectory: nil
227 file: @"New.sheepvm"
228 modalForWindow: [self window]
229 modalDelegate: self
230 didEndSelector: @selector(_newVirtualMachineDone: returnCode: contextInfo:)
231 contextInfo: nil];
232 }
233
234 - (IBAction) _newVirtualMachineDone: (NSSavePanel *) save returnCode: (int) returnCode contextInfo: (void *) contextInfo
235 {
236 if (returnCode == NSOKButton) {
237 NSFileManager *manager = [NSFileManager defaultManager];
238 [manager createDirectoryAtPath:[save filename] attributes:nil];
239 [manager createFileAtPath:[[save filename] stringByAppendingPathComponent:@"prefs"] contents:nil attributes:nil];
240 [vmArray addObject:[save filename]];
241 [vmList reloadData];
242 [vmList selectRow:([vmArray count] - 1) byExtendingSelection:NO];
243 [[VMSettingsController sharedInstance] editSettingsForNewVM:[save filename] sender:self];
244 if ([[VMSettingsController sharedInstance] cancelWasClicked]) {
245 [manager removeFileAtPath:[save filename] handler:nil];
246 [vmArray removeObjectAtIndex:([vmArray count] - 1)];
247 }
248 [self reloadDataAndSave];
249 }
250 }
251
252 - (IBAction) importVirtualMachine: (id) sender
253 {
254 [vmList abortEditing];
255 NSOpenPanel *open = [NSOpenPanel openPanel];
256 [open setMessage:@"Import SheepShaver Virtual Machine:"];
257 [open setResolvesAliases:YES];
258 // Curiously, bundles are treated as "files" not "directories" by NSOpenPanel.
259 [open setCanChooseDirectories:NO];
260 [open setCanChooseFiles:YES];
261 [open setAllowsMultipleSelection:NO];
262 [open beginSheetForDirectory: nil
263 file: nil
264 types: [NSArray arrayWithObject:@"sheepvm"]
265 modalForWindow: [self window]
266 modalDelegate: self
267 didEndSelector: @selector(_importVirtualMachineDone: returnCode: contextInfo:)
268 contextInfo: nil];
269 }
270
271 - (void) _importVirtualMachineDone: (NSOpenPanel *) open returnCode: (int) returnCode contextInfo: (void *) contextInfo
272 {
273 if (returnCode == NSOKButton) {
274 [vmArray addObject:[open filename]];
275 [self reloadDataAndSave];
276 }
277 }
278
279 - (IBAction) editVirtualMachineSettings: (id) sender
280 {
281 [vmList abortEditing];
282 int selectedRow = [vmList selectedRow];
283 if (selectedRow >= 0) {
284 NSString *path = [vmArray objectAtIndex:selectedRow];
285 if ([tasks objectForKey:path]) {
286 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
287 [alert setMessageText:@"Cannot edit virtual machine settings while it's running."];
288 [alert setAlertStyle:NSWarningAlertStyle];
289 [alert beginSheetModalForWindow:[self window]
290 modalDelegate:self
291 didEndSelector:nil
292 contextInfo:nil];
293 } else if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
294 [[VMSettingsController sharedInstance] editSettingsFor:path sender:sender];
295 } else {
296 [self _showNotFoundAlert];
297 }
298 }
299 }
300
301 - (IBAction) launchVirtualMachine: (id) sender
302 {
303 [vmList abortEditing];
304 int selectedRow = [vmList selectedRow];
305 if (selectedRow >= 0) {
306 NSString *path = [vmArray objectAtIndex:selectedRow];
307 if ([tasks objectForKey:path]) {
308 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
309 [alert setMessageText:@"The selected virtual machine is already running."];
310 [alert setAlertStyle:NSWarningAlertStyle];
311 [alert beginSheetModalForWindow:[self window]
312 modalDelegate:self
313 didEndSelector:nil
314 contextInfo:nil];
315 } else if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
316 NSTask *sheep = [[NSTask alloc] init];
317 [sheep setLaunchPath:[[NSBundle mainBundle] pathForAuxiliaryExecutable:@"SheepShaver"]];
318 [sheep setArguments:[NSArray arrayWithObject:path]];
319 [sheep launch];
320 [tasks setObject:sheep forKey:path];
321 } else {
322 [self _showNotFoundAlert];
323 }
324 }
325 }
326
327 - (void) onTaskTerminated: (NSNotification *) notification
328 {
329 NSArray *paths = [tasks allKeys];
330 NSEnumerator *enumerator = [paths objectEnumerator];
331 NSString *path;
332 while ((path = [enumerator nextObject])) {
333 NSTask *task = [tasks objectForKey:path];
334 if (![task isRunning]) {
335 [tasks removeObjectForKey:path];
336 [task release];
337 }
338 }
339 }
340
341 - (IBAction) deleteVirtualMachine: (id) sender
342 {
343 [vmList abortEditing];
344 int selectedRow = [vmList selectedRow];
345 if (selectedRow >= 0) {
346 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
347 [alert setMessageText:@"Do you wish to remove the selected virtual machine from the list?"];
348 [alert addButtonWithTitle:@"Remove"];
349 [alert addButtonWithTitle:@"Cancel"];
350 [alert setAlertStyle:NSWarningAlertStyle];
351 [alert beginSheetModalForWindow:[self window]
352 modalDelegate:self
353 didEndSelector:@selector(_deleteVirtualMachineDone: returnCode: contextInfo:)
354 contextInfo:nil];
355 }
356 }
357
358 - (void) _deleteVirtualMachineDone: (NSAlert *) alert returnCode: (int) returnCode contextInfo: (void *) contextInfo
359 {
360 if (returnCode == NSAlertFirstButtonReturn) {
361 [vmArray removeObjectAtIndex:[vmList selectedRow]];
362 [vmList deselectAll:self];
363 [self reloadDataAndSave];
364 }
365 }
366
367 - (IBAction) revealVirtualMachineInFinder: (id) sender
368 {
369 [vmList abortEditing];
370 int selectedRow = [vmList selectedRow];
371 if (selectedRow >= 0) {
372 NSString *path = [vmArray objectAtIndex:selectedRow];
373 if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
374 [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
375 } else {
376 [self _showNotFoundAlert];
377 }
378 }
379 }
380
381 @end