ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/MacOSX/Launcher/VMListController.mm
Revision: 1.9
Committed: 2009-08-26T07:09:06Z (14 years, 10 months ago) by asvitkine
Branch: MAIN
Changes since 1.8: +19 -8 lines
Log Message:
Launcher: Renaming VMs in list.

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