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, 9 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

# 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 asvitkine 1.11 [vmList abortEditing];
221 asvitkine 1.1 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 asvitkine 1.3 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 asvitkine 1.8 [[VMSettingsController sharedInstance] editSettingsForNewVM:[save filename] sender:self];
244 asvitkine 1.3 if ([[VMSettingsController sharedInstance] cancelWasClicked]) {
245     [manager removeFileAtPath:[save filename] handler:nil];
246     [vmArray removeObjectAtIndex:([vmArray count] - 1)];
247     }
248 asvitkine 1.8 [self reloadDataAndSave];
249 asvitkine 1.1 }
250     }
251    
252     - (IBAction) importVirtualMachine: (id) sender
253     {
254 asvitkine 1.11 [vmList abortEditing];
255 asvitkine 1.1 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 asvitkine 1.8 [self reloadDataAndSave];
276 asvitkine 1.1 }
277     }
278    
279 asvitkine 1.4 - (IBAction) editVirtualMachineSettings: (id) sender
280 asvitkine 1.1 {
281 asvitkine 1.11 [vmList abortEditing];
282 asvitkine 1.1 int selectedRow = [vmList selectedRow];
283 asvitkine 1.2 if (selectedRow >= 0) {
284 asvitkine 1.6 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 asvitkine 1.10 } else if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
294     [[VMSettingsController sharedInstance] editSettingsFor:path sender:sender];
295 asvitkine 1.6 } else {
296 asvitkine 1.10 [self _showNotFoundAlert];
297 asvitkine 1.6 }
298 asvitkine 1.2 }
299 asvitkine 1.1 }
300    
301 asvitkine 1.4 - (IBAction) launchVirtualMachine: (id) sender
302 asvitkine 1.1 {
303 asvitkine 1.11 [vmList abortEditing];
304 asvitkine 1.2 int selectedRow = [vmList selectedRow];
305     if (selectedRow >= 0) {
306 asvitkine 1.6 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 asvitkine 1.10 } else if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
316 asvitkine 1.6 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 asvitkine 1.10 } else {
322     [self _showNotFoundAlert];
323 asvitkine 1.6 }
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 asvitkine 1.2 }
339 asvitkine 1.1 }
340    
341 asvitkine 1.4 - (IBAction) deleteVirtualMachine: (id) sender
342     {
343 asvitkine 1.11 [vmList abortEditing];
344 asvitkine 1.4 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 asvitkine 1.6 }
356 asvitkine 1.4 }
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 asvitkine 1.8 [self reloadDataAndSave];
364 asvitkine 1.4 }
365     }
366    
367 asvitkine 1.7 - (IBAction) revealVirtualMachineInFinder: (id) sender
368     {
369 asvitkine 1.11 [vmList abortEditing];
370 asvitkine 1.7 int selectedRow = [vmList selectedRow];
371     if (selectedRow >= 0) {
372 asvitkine 1.10 NSString *path = [vmArray objectAtIndex:selectedRow];
373     if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
374     [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
375     } else {
376     [self _showNotFoundAlert];
377     }
378 asvitkine 1.7 }
379     }
380    
381 asvitkine 1.1 @end