ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/MacOSX/Launcher/VMListController.mm
Revision: 1.7
Committed: 2009-08-12T02:18:19Z (15 years, 1 month ago) by asvitkine
Branch: MAIN
Changes since 1.6: +85 -2 lines
Log Message:
re-arrange virtual machines by drag and drop
add a contextual menu that also allows to
reveal the selected virtual machine in finder

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