ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/MacOSX/Launcher/VMListController.mm
Revision: 1.10
Committed: 2009-08-31T05:08:21Z (15 years, 1 month ago) by asvitkine
Branch: MAIN
Changes since 1.9: +33 -9 lines
Log Message:
Launcher: Check if file exists.

File Contents

# User Rev Content
1 asvitkine 1.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 asvitkine 1.4 /*
25    
26     TODO:
27    
28     Drag VM from Finder to import
29 asvitkine 1.7 When choosing things like rom file and keycode files - have a checkbox to copy
30     selected file into the bundle.
31 asvitkine 1.8 Copy path!
32 asvitkine 1.4
33     */
34    
35 asvitkine 1.7 @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 asvitkine 1.1 @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 asvitkine 1.6 tasks = [[NSMutableDictionary alloc] init];
70    
71     [[NSNotificationCenter defaultCenter] addObserver:self
72     selector:@selector(onTaskTerminated:)
73     name:NSTaskDidTerminateNotification
74     object:nil];
75    
76 asvitkine 1.1 return self;
77     }
78    
79     - (void) awakeFromNib
80     {
81 asvitkine 1.9 [[[vmList tableColumns] objectAtIndex:0] setEditable:YES];
82 asvitkine 1.2 [vmList setDataSource: self];
83 asvitkine 1.4 [vmList setDelegate: self];
84 asvitkine 1.2 [vmList reloadData];
85 asvitkine 1.7 [vmList registerForDraggedTypes:[NSArray arrayWithObjects:VM_DRAG_TYPE, nil]];
86 asvitkine 1.1 }
87    
88 asvitkine 1.10 - (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 asvitkine 1.8 - (void) reloadDataAndSave
100     {
101     [vmList reloadData];
102     [[NSUserDefaults standardUserDefaults] setObject:vmArray forKey:@"vm_list"];
103     }
104    
105 asvitkine 1.4 - (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 asvitkine 1.1 - (int) numberOfRowsInTableView: (NSTableView *) table
116     {
117 asvitkine 1.2 return [vmArray count];
118 asvitkine 1.1 }
119    
120     - (id) tableView: (NSTableView *) table objectValueForTableColumn: (NSTableColumn *) c row: (int) r
121     {
122 asvitkine 1.9 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 asvitkine 1.10 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 asvitkine 1.9 }
141 asvitkine 1.1 }
142    
143 asvitkine 1.5 - (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 asvitkine 1.7 - (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 asvitkine 1.8 [self reloadDataAndSave];
186 asvitkine 1.7 [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 asvitkine 1.9 - (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 asvitkine 1.1
218     - (IBAction) newVirtualMachine: (id) sender
219     {
220     NSSavePanel *save = [NSSavePanel savePanel];
221     [save setMessage: @"New SheepShaver Virtual Machine:"];
222     [save setRequiredFileType: @"sheepvm"];
223     [save setCanSelectHiddenExtension: YES];
224     [save setExtensionHidden: NO];
225     [save beginSheetForDirectory: nil
226     file: @"New.sheepvm"
227     modalForWindow: [self window]
228     modalDelegate: self
229     didEndSelector: @selector(_newVirtualMachineDone: returnCode: contextInfo:)
230     contextInfo: nil];
231     }
232    
233     - (IBAction) _newVirtualMachineDone: (NSSavePanel *) save returnCode: (int) returnCode contextInfo: (void *) contextInfo
234     {
235     if (returnCode == NSOKButton) {
236 asvitkine 1.3 NSFileManager *manager = [NSFileManager defaultManager];
237     [manager createDirectoryAtPath:[save filename] attributes:nil];
238     [manager createFileAtPath:[[save filename] stringByAppendingPathComponent:@"prefs"] contents:nil attributes:nil];
239     [vmArray addObject:[save filename]];
240     [vmList reloadData];
241     [vmList selectRow:([vmArray count] - 1) byExtendingSelection:NO];
242 asvitkine 1.8 [[VMSettingsController sharedInstance] editSettingsForNewVM:[save filename] sender:self];
243 asvitkine 1.3 if ([[VMSettingsController sharedInstance] cancelWasClicked]) {
244     [manager removeFileAtPath:[save filename] handler:nil];
245     [vmArray removeObjectAtIndex:([vmArray count] - 1)];
246     }
247 asvitkine 1.8 [self reloadDataAndSave];
248 asvitkine 1.1 }
249     }
250    
251     - (IBAction) importVirtualMachine: (id) sender
252     {
253     NSOpenPanel *open = [NSOpenPanel openPanel];
254     [open setMessage:@"Import SheepShaver Virtual Machine:"];
255     [open setResolvesAliases:YES];
256     // Curiously, bundles are treated as "files" not "directories" by NSOpenPanel.
257     [open setCanChooseDirectories:NO];
258     [open setCanChooseFiles:YES];
259     [open setAllowsMultipleSelection:NO];
260     [open beginSheetForDirectory: nil
261     file: nil
262     types: [NSArray arrayWithObject:@"sheepvm"]
263     modalForWindow: [self window]
264     modalDelegate: self
265     didEndSelector: @selector(_importVirtualMachineDone: returnCode: contextInfo:)
266     contextInfo: nil];
267     }
268    
269     - (void) _importVirtualMachineDone: (NSOpenPanel *) open returnCode: (int) returnCode contextInfo: (void *) contextInfo
270     {
271     if (returnCode == NSOKButton) {
272     [vmArray addObject:[open filename]];
273 asvitkine 1.8 [self reloadDataAndSave];
274 asvitkine 1.1 }
275     }
276    
277 asvitkine 1.4 - (IBAction) editVirtualMachineSettings: (id) sender
278 asvitkine 1.1 {
279     int selectedRow = [vmList selectedRow];
280 asvitkine 1.2 if (selectedRow >= 0) {
281 asvitkine 1.6 NSString *path = [vmArray objectAtIndex:selectedRow];
282     if ([tasks objectForKey:path]) {
283     NSAlert *alert = [[[NSAlert alloc] init] autorelease];
284     [alert setMessageText:@"Cannot edit virtual machine settings while it's running."];
285     [alert setAlertStyle:NSWarningAlertStyle];
286     [alert beginSheetModalForWindow:[self window]
287     modalDelegate:self
288     didEndSelector:nil
289     contextInfo:nil];
290 asvitkine 1.10 } else if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
291     [[VMSettingsController sharedInstance] editSettingsFor:path sender:sender];
292 asvitkine 1.6 } else {
293 asvitkine 1.10 [self _showNotFoundAlert];
294 asvitkine 1.6 }
295 asvitkine 1.2 }
296 asvitkine 1.1 }
297    
298 asvitkine 1.4 - (IBAction) launchVirtualMachine: (id) sender
299 asvitkine 1.1 {
300 asvitkine 1.2 int selectedRow = [vmList selectedRow];
301     if (selectedRow >= 0) {
302 asvitkine 1.6 NSString *path = [vmArray objectAtIndex:selectedRow];
303     if ([tasks objectForKey:path]) {
304     NSAlert *alert = [[[NSAlert alloc] init] autorelease];
305     [alert setMessageText:@"The selected virtual machine is already running."];
306     [alert setAlertStyle:NSWarningAlertStyle];
307     [alert beginSheetModalForWindow:[self window]
308     modalDelegate:self
309     didEndSelector:nil
310     contextInfo:nil];
311 asvitkine 1.10 } else if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
312 asvitkine 1.6 NSTask *sheep = [[NSTask alloc] init];
313     [sheep setLaunchPath:[[NSBundle mainBundle] pathForAuxiliaryExecutable:@"SheepShaver"]];
314     [sheep setArguments:[NSArray arrayWithObject:path]];
315     [sheep launch];
316     [tasks setObject:sheep forKey:path];
317 asvitkine 1.10 } else {
318     [self _showNotFoundAlert];
319 asvitkine 1.6 }
320     }
321     }
322    
323     - (void) onTaskTerminated: (NSNotification *) notification
324     {
325     NSArray *paths = [tasks allKeys];
326     NSEnumerator *enumerator = [paths objectEnumerator];
327     NSString *path;
328     while ((path = [enumerator nextObject])) {
329     NSTask *task = [tasks objectForKey:path];
330     if (![task isRunning]) {
331     [tasks removeObjectForKey:path];
332     [task release];
333     }
334 asvitkine 1.2 }
335 asvitkine 1.1 }
336    
337 asvitkine 1.4 - (IBAction) deleteVirtualMachine: (id) sender
338     {
339     int selectedRow = [vmList selectedRow];
340     if (selectedRow >= 0) {
341     NSAlert *alert = [[[NSAlert alloc] init] autorelease];
342     [alert setMessageText:@"Do you wish to remove the selected virtual machine from the list?"];
343     [alert addButtonWithTitle:@"Remove"];
344     [alert addButtonWithTitle:@"Cancel"];
345     [alert setAlertStyle:NSWarningAlertStyle];
346     [alert beginSheetModalForWindow:[self window]
347     modalDelegate:self
348     didEndSelector:@selector(_deleteVirtualMachineDone: returnCode: contextInfo:)
349     contextInfo:nil];
350 asvitkine 1.6 }
351 asvitkine 1.4 }
352    
353     - (void) _deleteVirtualMachineDone: (NSAlert *) alert returnCode: (int) returnCode contextInfo: (void *) contextInfo
354     {
355     if (returnCode == NSAlertFirstButtonReturn) {
356     [vmArray removeObjectAtIndex:[vmList selectedRow]];
357     [vmList deselectAll:self];
358 asvitkine 1.8 [self reloadDataAndSave];
359 asvitkine 1.4 }
360     }
361    
362 asvitkine 1.7 - (IBAction) revealVirtualMachineInFinder: (id) sender
363     {
364     int selectedRow = [vmList selectedRow];
365     if (selectedRow >= 0) {
366 asvitkine 1.10 NSString *path = [vmArray objectAtIndex:selectedRow];
367     if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
368     [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
369     } else {
370     [self _showNotFoundAlert];
371     }
372 asvitkine 1.7 }
373     }
374    
375 asvitkine 1.1 @end