ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/MacOSX/Launcher/VMListController.mm
Revision: 1.8
Committed: 2009-08-18T03:42:11Z (15 years, 1 month ago) by asvitkine
Branch: MAIN
Changes since 1.7: +12 -10 lines
Log Message:
Launcher: Use default settings from NIB for new VM.

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     Verify if VM exists
29     Drag VM from Finder to import
30     Don't show Preferences menu in spawned SheepShaver instances - or make them
31     use the same nib file as this app!
32 asvitkine 1.7 When choosing things like rom file and keycode files - have a checkbox to copy
33     selected file into the bundle.
34 asvitkine 1.8 Copy path!
35 asvitkine 1.4
36     */
37    
38 asvitkine 1.7 @interface NSObject (TableViewContextMenu)
39     - (NSMenu *) tableView: (NSTableView *) tableView menuForEvent: (NSEvent *) event;
40     @end
41    
42     @implementation NSTableView (ContextMenu)
43     - (NSMenu *) menuForEvent: (NSEvent *) event
44     {
45     if ([[self delegate] respondsToSelector:@selector(tableView:menuForEvent:)])
46     return [[self delegate] tableView:self menuForEvent:event];
47     return nil;
48     }
49     @end
50    
51     #define VM_DRAG_TYPE @"sheepvm"
52    
53 asvitkine 1.1 @implementation VMListController
54    
55     + (id) sharedInstance
56     {
57     static VMListController *_sharedInstance = nil;
58     if (!_sharedInstance) {
59     _sharedInstance = [[VMListController allocWithZone:[self zone]] init];
60     }
61     return _sharedInstance;
62     }
63    
64     - (id) init
65     {
66     self = [super initWithWindowNibName:@"VMListWindow"];
67    
68     NSArray *vms = [[NSUserDefaults standardUserDefaults] stringArrayForKey:@"vm_list"];
69     vmArray = [[NSMutableArray alloc] initWithCapacity:[vms count]];
70     [vmArray addObjectsFromArray:vms];
71    
72 asvitkine 1.6 tasks = [[NSMutableDictionary alloc] init];
73    
74     [[NSNotificationCenter defaultCenter] addObserver:self
75     selector:@selector(onTaskTerminated:)
76     name:NSTaskDidTerminateNotification
77     object:nil];
78    
79 asvitkine 1.1 return self;
80     }
81    
82     - (void) awakeFromNib
83     {
84 asvitkine 1.2 [vmList setDataSource: self];
85 asvitkine 1.4 [vmList setDelegate: self];
86 asvitkine 1.2 [vmList reloadData];
87 asvitkine 1.7 [vmList registerForDraggedTypes:[NSArray arrayWithObjects:VM_DRAG_TYPE, nil]];
88 asvitkine 1.1 }
89    
90 asvitkine 1.8 - (void) reloadDataAndSave
91     {
92     [vmList reloadData];
93     [[NSUserDefaults standardUserDefaults] setObject:vmArray forKey:@"vm_list"];
94     }
95    
96 asvitkine 1.4 - (void) keyDown: (NSEvent *) event
97     {
98     if ([event type] == NSKeyDown && [[event characters] length] > 0) {
99     unichar key = [[event characters] characterAtIndex:0];
100     if (key == NSDeleteFunctionKey || key == NSDeleteCharacter) {
101     [self deleteVirtualMachine:self];
102     }
103     }
104     }
105    
106 asvitkine 1.1 - (int) numberOfRowsInTableView: (NSTableView *) table
107     {
108 asvitkine 1.2 return [vmArray count];
109 asvitkine 1.1 }
110    
111     - (id) tableView: (NSTableView *) table objectValueForTableColumn: (NSTableColumn *) c row: (int) r
112     {
113 asvitkine 1.2 return [vmArray objectAtIndex: r]; // [[vmArray objectAtIndex: r] lastPathComponent];
114 asvitkine 1.1 }
115    
116 asvitkine 1.5 - (void) tableViewSelectionDidChange: (NSNotification *) notification
117     {
118     if ([vmList selectedRow] >= 0) {
119     [settingsButton setEnabled:YES];
120     [launchButton setEnabled:YES];
121     } else {
122     [settingsButton setEnabled:NO];
123     [launchButton setEnabled:NO];
124     }
125     }
126    
127 asvitkine 1.7 - (BOOL) tableView: (NSTableView *) table writeRowsWithIndexes: (NSIndexSet *) rows toPasteboard: (NSPasteboard *) pboard
128     {
129     vmBeingDragged = [vmArray objectAtIndex:[rows firstIndex]];
130     [pboard declareTypes:[NSArray arrayWithObject:VM_DRAG_TYPE] owner:self];
131     [pboard setString:VM_DRAG_TYPE forType:VM_DRAG_TYPE];
132     return YES;
133     }
134    
135     - (NSDragOperation) tableView: (NSTableView *) table validateDrop: (id <NSDraggingInfo>) info proposedRow: (int) row proposedDropOperation: (NSTableViewDropOperation) op
136     {
137     if (op == NSTableViewDropAbove && row != -1) {
138     return NSDragOperationPrivate;
139     } else {
140     return NSDragOperationNone;
141     }
142     }
143    
144     - (BOOL) tableView: (NSTableView *) table acceptDrop: (id <NSDraggingInfo>) info row: (int) row dropOperation: (NSTableViewDropOperation) op
145     {
146     if ([[[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject:VM_DRAG_TYPE]] isEqualToString:VM_DRAG_TYPE]) {
147     [vmList deselectAll:nil];
148     int index = [vmArray indexOfObject:vmBeingDragged];
149     if (index != row) {
150     [vmArray insertObject:vmBeingDragged atIndex:row];
151     if (row <= index) {
152     index += 1;
153     } else {
154     row -= 1;
155     }
156     [vmArray removeObjectAtIndex: index];
157     }
158 asvitkine 1.8 [self reloadDataAndSave];
159 asvitkine 1.7 [vmList selectRow:row byExtendingSelection:NO];
160     return YES;
161     }
162    
163     return NO;
164     }
165    
166     - (NSMenu *) tableView: (NSTableView *) table menuForEvent: (NSEvent *) event
167     {
168     NSMenu *menu = nil;
169     int row = [table rowAtPoint:[table convertPoint:[event locationInWindow] fromView:nil]];
170     if (row >= 0) {
171     [table selectRow:row byExtendingSelection:NO];
172     menu = [[[NSMenu alloc] initWithTitle: @"Contextual Menu"] autorelease];
173     [menu addItemWithTitle: @"Launch Virtual Machine"
174     action: @selector(launchVirtualMachine:) keyEquivalent: @""];
175     [menu addItemWithTitle: @"Edit VM Settings..."
176     action: @selector(editVirtualMachineSettings:) keyEquivalent: @""];
177     [menu addItemWithTitle: @"Reveal VM in Finder"
178     action: @selector(revealVirtualMachineInFinder:) keyEquivalent: @""];
179     [menu addItemWithTitle: @"Remove VM from List"
180     action: @selector(deleteVirtualMachine:) keyEquivalent: @""];
181     }
182     return menu;
183     }
184    
185 asvitkine 1.1 //- (NSString *) tableView: (NSTableView *) table toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
186     // tableColumn: (NSTableColumn *) c row: (int) r mouseLocation: (NSPoint) loc
187     //{
188     // return [vmArray objectAtIndex: r];
189     //}
190    
191     - (IBAction) newVirtualMachine: (id) sender
192     {
193     NSSavePanel *save = [NSSavePanel savePanel];
194     [save setMessage: @"New SheepShaver Virtual Machine:"];
195     [save setRequiredFileType: @"sheepvm"];
196     [save setCanSelectHiddenExtension: YES];
197     [save setExtensionHidden: NO];
198     [save beginSheetForDirectory: nil
199     file: @"New.sheepvm"
200     modalForWindow: [self window]
201     modalDelegate: self
202     didEndSelector: @selector(_newVirtualMachineDone: returnCode: contextInfo:)
203     contextInfo: nil];
204     }
205    
206     - (IBAction) _newVirtualMachineDone: (NSSavePanel *) save returnCode: (int) returnCode contextInfo: (void *) contextInfo
207     {
208     if (returnCode == NSOKButton) {
209 asvitkine 1.3 NSFileManager *manager = [NSFileManager defaultManager];
210     [manager createDirectoryAtPath:[save filename] attributes:nil];
211     [manager createFileAtPath:[[save filename] stringByAppendingPathComponent:@"prefs"] contents:nil attributes:nil];
212     [vmArray addObject:[save filename]];
213     [vmList reloadData];
214     [vmList selectRow:([vmArray count] - 1) byExtendingSelection:NO];
215 asvitkine 1.8 [[VMSettingsController sharedInstance] editSettingsForNewVM:[save filename] sender:self];
216 asvitkine 1.3 if ([[VMSettingsController sharedInstance] cancelWasClicked]) {
217     [manager removeFileAtPath:[save filename] handler:nil];
218     [vmArray removeObjectAtIndex:([vmArray count] - 1)];
219     }
220 asvitkine 1.8 [self reloadDataAndSave];
221 asvitkine 1.1 }
222     }
223    
224     - (IBAction) importVirtualMachine: (id) sender
225     {
226     NSOpenPanel *open = [NSOpenPanel openPanel];
227     [open setMessage:@"Import SheepShaver Virtual Machine:"];
228     [open setResolvesAliases:YES];
229     // Curiously, bundles are treated as "files" not "directories" by NSOpenPanel.
230     [open setCanChooseDirectories:NO];
231     [open setCanChooseFiles:YES];
232     [open setAllowsMultipleSelection:NO];
233     [open beginSheetForDirectory: nil
234     file: nil
235     types: [NSArray arrayWithObject:@"sheepvm"]
236     modalForWindow: [self window]
237     modalDelegate: self
238     didEndSelector: @selector(_importVirtualMachineDone: returnCode: contextInfo:)
239     contextInfo: nil];
240     }
241    
242     - (void) _importVirtualMachineDone: (NSOpenPanel *) open returnCode: (int) returnCode contextInfo: (void *) contextInfo
243     {
244     if (returnCode == NSOKButton) {
245     [vmArray addObject:[open filename]];
246 asvitkine 1.8 [self reloadDataAndSave];
247 asvitkine 1.1 }
248     }
249    
250 asvitkine 1.4 - (IBAction) editVirtualMachineSettings: (id) sender
251 asvitkine 1.1 {
252     int selectedRow = [vmList selectedRow];
253 asvitkine 1.2 if (selectedRow >= 0) {
254 asvitkine 1.6 NSString *path = [vmArray objectAtIndex:selectedRow];
255     if ([tasks objectForKey:path]) {
256     NSAlert *alert = [[[NSAlert alloc] init] autorelease];
257     [alert setMessageText:@"Cannot edit virtual machine settings while it's running."];
258     [alert setAlertStyle:NSWarningAlertStyle];
259     [alert beginSheetModalForWindow:[self window]
260     modalDelegate:self
261     didEndSelector:nil
262     contextInfo:nil];
263     } else {
264     [[VMSettingsController sharedInstance] editSettingsFor:path sender:sender];
265     }
266 asvitkine 1.2 }
267 asvitkine 1.1 }
268    
269 asvitkine 1.4 - (IBAction) launchVirtualMachine: (id) sender
270 asvitkine 1.1 {
271 asvitkine 1.2 int selectedRow = [vmList selectedRow];
272     if (selectedRow >= 0) {
273 asvitkine 1.6 NSString *path = [vmArray objectAtIndex:selectedRow];
274     if ([tasks objectForKey:path]) {
275     NSAlert *alert = [[[NSAlert alloc] init] autorelease];
276     [alert setMessageText:@"The selected virtual machine is already running."];
277     [alert setAlertStyle:NSWarningAlertStyle];
278     [alert beginSheetModalForWindow:[self window]
279     modalDelegate:self
280     didEndSelector:nil
281     contextInfo:nil];
282     } else {
283     NSTask *sheep = [[NSTask alloc] init];
284     [sheep setLaunchPath:[[NSBundle mainBundle] pathForAuxiliaryExecutable:@"SheepShaver"]];
285     [sheep setArguments:[NSArray arrayWithObject:path]];
286     [sheep launch];
287     [tasks setObject:sheep forKey:path];
288     }
289     }
290     }
291    
292     - (void) onTaskTerminated: (NSNotification *) notification
293     {
294     NSArray *paths = [tasks allKeys];
295     NSEnumerator *enumerator = [paths objectEnumerator];
296     NSString *path;
297     while ((path = [enumerator nextObject])) {
298     NSTask *task = [tasks objectForKey:path];
299     if (![task isRunning]) {
300     [tasks removeObjectForKey:path];
301     [task release];
302     }
303 asvitkine 1.2 }
304 asvitkine 1.1 }
305    
306 asvitkine 1.4 - (IBAction) deleteVirtualMachine: (id) sender
307     {
308     int selectedRow = [vmList selectedRow];
309     if (selectedRow >= 0) {
310     NSAlert *alert = [[[NSAlert alloc] init] autorelease];
311     [alert setMessageText:@"Do you wish to remove the selected virtual machine from the list?"];
312     [alert addButtonWithTitle:@"Remove"];
313     [alert addButtonWithTitle:@"Cancel"];
314     [alert setAlertStyle:NSWarningAlertStyle];
315     [alert beginSheetModalForWindow:[self window]
316     modalDelegate:self
317     didEndSelector:@selector(_deleteVirtualMachineDone: returnCode: contextInfo:)
318     contextInfo:nil];
319    
320 asvitkine 1.6 }
321 asvitkine 1.4 }
322    
323     - (void) _deleteVirtualMachineDone: (NSAlert *) alert returnCode: (int) returnCode contextInfo: (void *) contextInfo
324     {
325     if (returnCode == NSAlertFirstButtonReturn) {
326     [vmArray removeObjectAtIndex:[vmList selectedRow]];
327     [vmList deselectAll:self];
328 asvitkine 1.8 [self reloadDataAndSave];
329 asvitkine 1.4 }
330     }
331    
332 asvitkine 1.7 - (IBAction) revealVirtualMachineInFinder: (id) sender
333     {
334     int selectedRow = [vmList selectedRow];
335     if (selectedRow >= 0) {
336     [[NSWorkspace sharedWorkspace] selectFile: [vmArray objectAtIndex:selectedRow] inFileViewerRootedAtPath: @""];
337     }
338     }
339    
340 asvitkine 1.1 @end