root/branches/0_9_1/qcontrol/cocoaControlController.m

Revision 93, 101.9 KB (checked in by mike, 3 years ago)

[fix] remove crashed/stray guests from doServer (#15)
[fix] Q Control: StopPC Icon is a forcequit if task is not responding or doServer is not responding (#15)
[fix] Fullscreen: alt-tabing back to Q brings you back to FS Guest (#32)
[fix] Fullscreen: switching animation always vertical
[fix] Fullscreen: honor aspect ratio, only factors of 1/1.25/1.5/1.75/2.0/3.0/4.0... (#18)
[fix] doServer: added missing dealloc
[fix] quartz: panther compiling
[fix] quartz: small drawspeed improvements
[fix] backport bgr support for bigendians (andreasf) (#14)
[new] Q Control: EditPC can be used (read only) when guest is running (#23)
[new] Q Control: commandline as tooltips of guests (#23)
[new] Q icon

Line 
1/*
2 * QEMU Cocoa Control Controller
3 *
4 * Copyright (c) 2005 - 2007 Mike Kronenberg
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#import "../host-cocoa/CGSPrivate.h"
26
27#import "cocoaControlController.h"
28#import "cocoaControlDiskImage.h"
29#import "cocoaControlNewPCAssistant.h"
30
31@implementation cocoaControlController
32-(id)init
33{
34//  NSLog(@"cocoaControlController: init");
35
36    /* preferences */
37    [[NSUserDefaults standardUserDefaults] registerDefaults:[[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:
38        [NSString stringWithString:@"Quartz"], /* enable Quartz by default */
39        [NSNumber numberWithBool:TRUE], /* enable search for updates */
40        [NSNumber numberWithBool:FALSE], /* disable log to console */
41        [@"~/Documents/QEMU" stringByExpandingTildeInPath], /* standart path */
42        nil
43    ] forKeys:[NSArray arrayWithObjects:@"display", @"enableCheckForUpdates", @"enableLogToConsole", @"dataPath", nil]]];
44    userDefaults = [NSUserDefaults standardUserDefaults];
45
46    /* compatibility with old prefferences */
47    if ([userDefaults objectForKey:@"enableOpenGL"]) {
48        if (![userDefaults boolForKey:@"enableOpenGL"]) {
49            [userDefaults setObject:@"QuickDraw" forKey:@"display"];
50        }
51        [userDefaults removeObjectForKey:@"enableOpenGL"];
52    }
53
54    if ((self = [super init])) {
55    [[NSNotificationCenter defaultCenter] addObserver:self
56        selector:@selector(checkATaskStatus:)
57        name:NSTaskDidTerminateNotification
58        object:nil];
59
60    /* check for update */
61    if ([userDefaults boolForKey:@"enableCheckForUpdates"]) {
62        [self getLatestVersion];
63    }
64
65    /* start qserver for distributed object */
66    qdoserver = [[cocoaControlDOServer alloc] init];
67    [qdoserver setSender:self];
68
69    return self;
70    }
71
72    return nil;
73}
74
75-(id)pcs
76{
77    return pcs;
78}
79
80- (id)pcsTasks
81{
82    return pcsTasks;
83}
84
85/* NSApp Delegate */
86- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
87{
88//  NSLog(@"cocoaControlController: openFile");
89
90    [ self startPC:filename];
91   
92    return true;
93}
94
95-(void)awakeFromNib
96{
97//  NSLog(@"cocoaControlController: awakeFromNib");
98   
99    [NSApp setDelegate:self];
100    [mainWindow setDelegate:self];
101
102    /* other Nibs */
103    editPC = [[cocoaControlEditPC alloc] init];
104    if (![NSBundle loadNibNamed:@"cocoaControlEditPC" owner:editPC]) {
105        printf("cocoaControlEditPC.nib not loaded!\n");
106    }
107    [[editPC editPCPanel] center];
108
109    preferences = [[cocoaControlPreferences alloc] init];
110    if (![NSBundle loadNibNamed:@"cocoaControlPreferences" owner:preferences]) {
111        printf("cocoaControlPreferences.nib not loaded!\n");
112    }
113
114    /* div vars */
115    if (![userDefaults objectForKey:@"dataPath"]) { /* here we take care of older userDefaults without @"dataPath" */
116        [userDefaults setObject:[NSString stringWithFormat:@"%@/Documents/QEMU", NSHomeDirectory()] forKey:@"dataPath"];
117    }
118
119    cpuTypes = [[[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:@"i386-softmmu",@"x86_64-softmmu",@"ppc-softmmu",@"sparc-softmmu",@"mips-softmmu",@"arm-softmmu",nil] forKeys:[NSArray arrayWithObjects:@"x86",@"x86-64",@"PowerPC",@"SPARC",@"MIPS",@"ARM",nil]] retain];
120
121    /* create PC directory */
122    NSFileManager *fileManager = [NSFileManager defaultManager];
123    if ([fileManager fileExistsAtPath: [NSString stringWithFormat:@"%@/", [userDefaults objectForKey:@"dataPath"]]] == NO)
124        [fileManager createDirectoryAtPath: [NSString stringWithFormat:@"%@/", [userDefaults objectForKey:@"dataPath"]] attributes: nil];
125
126    /* initialise pcs */
127    [self loadConfigurations];
128   
129    /* change status to "shutdown" after corrupt termination of QEMU */
130    int i;
131    for (i=0; i < [pcs count]; i++)
132    if ([[[[pcs objectAtIndex:i] objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"running"] ) {
133            [[[pcs objectAtIndex:i] objectForKey:@"PC Data"] setObject:@"shutdown" forKey:@"state"];
134            [self savePCConfiguration:[pcs objectAtIndex:i]];
135    }
136
137    /* Creating Toolbar */
138    NSToolbar *controlWindowToolbar = [[[NSToolbar alloc] initWithIdentifier: @"controlWindowToolbarIdentifier"] autorelease];
139    [controlWindowToolbar setAllowsUserCustomization: YES]; //allow customisation
140    [controlWindowToolbar setAutosavesConfiguration: YES]; //autosave changes
141    [controlWindowToolbar setDisplayMode: NSToolbarDisplayModeIconOnly]; //what is shown
142    [controlWindowToolbar setSizeMode:NSToolbarSizeModeSmall]; //default Toolbar Size
143    [controlWindowToolbar setDelegate: self]; // We are the delegate
144    [mainWindow setToolbar: controlWindowToolbar]; // Attach the toolbar to the document window
145
146    /* format Cell to imageCell */
147    id cell;
148    NSTableColumn *theColumn;
149    theColumn = [table tableColumnWithIdentifier:@"image"];
150    cell = [NSImageCell new];
151    [theColumn setDataCell:cell];
152
153    /* set infos for microIcons */
154    [table setQControl:self];
155
156    /* handle Table DoubleClick */
157    [table setTarget:self];
158    [table setDoubleAction:@selector(tableDoubleClick:)];
159
160    /* Timer for Updates */
161    timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector( updateThumbnails ) userInfo:nil repeats:YES];
162
163    /* Dictionary for Tasks */
164    pcsTasks = [[NSMutableDictionary alloc] init];
165    pcsPIDs = [[NSMutableDictionary alloc] init];
166
167    /* Dictionary for Windows Numbers */
168    pcsWindows = [[NSMutableArray alloc] init];
169
170    /* loading initial Thumbnails */
171    [self updateThumbnails];
172
173    /* register table for drag'n drop */
174    [table registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
175}
176
177
178
179- (void) applicationWillBecomeActive:(NSNotification *)aNotification
180{
181//  NSLog(@"applicationWillBecomeActive: applicationWillBecomeActive");
182
183    NSEnumerator *enumerator = [pcsPIDs keyEnumerator];
184    id key;
185    NSMutableDictionary *pcsWindowsNumbers = [[NSMutableDictionary alloc] init];
186
187    while ((key = [enumerator nextObject])) {
188        [pcsWindowsNumbers setObject:
189            [[[pcsPIDs objectForKey:key] objectForKey:@"PC Data"] objectForKey:@"name"]
190        forKey:
191            [NSString stringWithFormat:@"%d",[qdoserver guestWindowNumber:[[[pcsPIDs objectForKey:key] objectForKey:@"PC Data"] objectForKey:@"name"]]]
192        ];
193    }
194
195    int wcount;
196    NSCountWindows(&wcount);
197    int wlist[wcount];
198    NSWindowList(wcount, wlist);
199    [pcsWindows removeAllObjects];
200    int i;
201
202    for (i=0; i<wcount; i++) {
203        if ([pcsWindowsNumbers objectForKey:[NSString stringWithFormat:@"%d",wlist[i]]]) {
204            [pcsWindows addObject:[pcsWindowsNumbers objectForKey:[NSString stringWithFormat:@"%d",wlist[i]]]];
205        } else if (wlist[i]==[mainWindow windowNumber]) {
206            [pcsWindows addObject:@"Q Control"];
207        }
208       
209    }
210   
211}
212
213- (void) applicationDidBecomeActive:(NSNotification *)aNotification
214{
215//  NSLog(@"cocoaControlController: applicationDidBecomeActive");
216
217    int i;
218    int aboveWindowNumber = [mainWindow windowNumber];
219
220    if ([mainWindow isKeyWindow]) {
221
222        /* set Key Window */
223        if (![[pcsWindows objectAtIndex:0] isEqual:@"Q Control"]) {
224            ProcessSerialNumber psn;
225            GetProcessForPID( [[pcsTasks objectForKey:[pcsWindows objectAtIndex:0]] processIdentifier], &psn );
226            SetFrontProcess( &psn );
227            aboveWindowNumber = [qdoserver guestWindowNumber:[pcsWindows objectAtIndex:0]];
228        }
229
230        /* set other Windows */
231        for (i=1; i<[pcsWindows count]; i++) {/* displaying windows with the lowest possible flicker */
232            if ([[pcsWindows objectAtIndex:i] isEqual:@"Q Control"]) {
233                aboveWindowNumber = [mainWindow windowNumber];
234            } else {
235                [qdoserver guestOrderWindow:NSWindowBelow relativeTo:aboveWindowNumber guest:[pcsWindows objectAtIndex:i]];
236                aboveWindowNumber = [qdoserver guestWindowNumber:[pcsWindows objectAtIndex:i]];
237            }
238        }
239
240        [pcsWindows removeAllObjects];
241
242        /* is there a current FS guest? */
243        if ([qdoserver lastGuestDeactivated]) {
244
245             /* setup transition */
246            CGSConnection cid = _CGSDefaultConnection();
247            int transitionHandle = -1;
248            CGSTransitionSpec transitionSpecifications;
249
250            transitionSpecifications.type = 7;          //transition;
251            transitionSpecifications.option = 8;        //option;
252            transitionSpecifications.wid = 0;           //wid
253            transitionSpecifications.backColour = 0;    //background color
254
255            /* freeze desktop: OSStatus CGSNewTransition(const CGSConnection cid, const CGSTransitionSpec* transitionSpecifications, int *transitionHandle) */
256            CGSNewTransition(cid, &transitionSpecifications, &transitionHandle);
257
258            /* change monitor */
259            [qdoserver guestUnhide:[qdoserver lastGuestDeactivated]]; //show hidden window
260
261            /* make us front */
262            ProcessSerialNumber psn;
263            GetProcessForPID( [[pcsTasks objectForKey:[qdoserver lastGuestDeactivated]] processIdentifier], &psn );
264            SetFrontProcess( &psn );
265
266            /* wait */
267            usleep(10000);
268
269            /* run transition: OSStatus CGSInvokeTransition(const CGSConnection cid, int transitionHandle, float duration) */
270            CGSInvokeTransition(cid, transitionHandle, 1.0);
271
272            /* reset lastGuest */
273            [qdoserver guestDeactivated:nil];
274
275        }
276    }
277}
278
279- (IBAction) activateApp:(id)sender
280{
281//  NSLog(@"cocoaControlController: activateApp");
282    if (![NSApp isActive])
283        [NSApp activateIgnoringOtherApps:YES];
284}
285
286- (void) applicationWillHide:(NSNotification *)aNotification
287{
288//  NSLog(@"cocoaControlController: applicationWillHide");
289
290    NSEnumerator *enumerator = [pcsPIDs keyEnumerator];
291    id key;
292    while ((key = [enumerator nextObject])) {
293        [qdoserver guestHide:[[[pcsPIDs objectForKey:key] objectForKey:@"PC Data"] objectForKey:@"name"]];
294    }
295}
296
297- (void) applicationWillUnhide:(NSNotification *)aNotification
298{
299//  NSLog(@"cocoaControlController: applicationWillUnhide");
300
301    NSEnumerator *enumerator = [pcsPIDs keyEnumerator];
302    id key;
303    while ((key = [enumerator nextObject])) {
304        [qdoserver guestUnhide:[[[pcsPIDs objectForKey:key] objectForKey:@"PC Data"] objectForKey:@"name"]];
305    }
306}
307
308- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
309{
310//  NSLog(@"cocoaControlController: applicationShouldTerminate");
311
312    if ([pcsPIDs count]) {
313        [self standardAlert: NSLocalizedStringFromTable(@"applicationShouldTerminate:standardAlert", @"Localizable", @"cocoaControlController")
314             informativeText: NSLocalizedStringFromTable(@"applicationShouldTerminate:informativeText", @"Localizable", @"cocoaControlController")];
315        return NSTerminateCancel;
316    }
317
318    return NSTerminateNow;
319}
320
321-(void) applicationWillTerminate:(NSNotification *)notification
322{
323//  NSLog(@"cocoaControlController: applicationWillTerminate");
324
325    /* cleanup */
326    [pcsPIDs release];
327    [pcsTasks release];
328    [pcs release];
329    [pcsImages release];
330    [pcsWindows release];
331}
332
333/* mainMenu */
334- (IBAction) showPreferences:(id)sender
335{
336//  NSLog(@"cocoaControlController: showPreferences");
337
338    /* enter current Values into preferencesPanel */
339    [preferences preparePreferences:self];
340
341    /* display preferencesPanel */
342    [[preferences preferencesPanel] setDelegate:preferences];
343    [[preferences preferencesPanel] center];
344    [[preferences preferencesPanel] makeKeyAndOrderFront:self];
345}
346
347- (IBAction) qemuWindowMoveToFront:(id)sender
348{
349//  NSLog(@"cocoaControlController: qemuWindowMoveToFront");
350
351    ProcessSerialNumber psn;
352
353    /* move a QEMU to front */
354    GetProcessForPID( [sender tag], &psn );
355    SetFrontProcess( &psn );
356}
357
358/* NSWindow Delegate */
359- (IBAction) cycleWindows:(id)sender
360{
361//    NSLog(@"cocoaControlController: cycleWindows");
362    [qdoserver guestSwitch: @"Q Control" fullscreen:NO nextGuestName:nil];
363}
364
365- (IBAction) cycleWindowsBack:(id)sender
366{
367//    NSLog(@"cocoaControlController: cycleWindows");
368   
369    [qdoserver guestSwitch: @"Q Control" fullscreen:NO previousGuestName:nil];
370}
371
372- (BOOL) windowShouldClose:(id)sender
373{
374//  NSLog(@"cocoaControlController: windowShouldClose");
375   
376    [NSApp terminate:nil];
377    return NO;
378}
379
380
381- (void) checkATaskStatus:(NSNotification *)aNotification
382{
383//  NSLog(@"cocoaControlController: checkATaskStatus");
384
385    id thisPC;
386
387    thisPC = [pcsPIDs objectForKey:[NSString stringWithFormat:@"%d", [[aNotification object] processIdentifier]]];
388    if (!thisPC)
389        return;
390
391    int status = [[aNotification object] terminationStatus];
392    if (status == 0) {
393        [[thisPC objectForKey:@"PC Data"] setObject:@"shutdown" forKey:@"state"];
394    } else if (status == 2) {
395        [[thisPC objectForKey:@"PC Data"] setObject:@"saved" forKey:@"state"];
396    } else {
397        // something is wrong here :-)
398        [[thisPC objectForKey:@"PC Data"] setObject:@"shutdown" forKey:@"state"];
399
400        /* error management here */
401        /* display the crash output of qemu */
402        if(![userDefaults boolForKey:@"enableLogToConsole"]) {
403
404            NSData * pipedata;
405
406            while ((pipedata = [[[[pcsTasks objectForKey:[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]] standardOutput] fileHandleForReading] availableData]) && [pipedata length])
407            {
408                NSString * console_out = [[[NSString alloc] initWithData:pipedata encoding:NSUTF8StringEncoding] autorelease];
409                // trim string to only contain the error
410                NSArray * comps = [console_out componentsSeparatedByString:@": "];
411                NSString * errormsg = [@"Error: " stringByAppendingString:[comps objectAtIndex:1]];
412                [self standardAlert:@"Qemu unexpectedly quit" informativeText:errormsg];
413            }
414        }
415    }
416
417    /* remove stray guest from qdoserver */
418    if ([[qdoserver guests] objectForKey:[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]) {
419        [qdoserver guestUnregisterWithName:[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]];
420    }
421
422    /* Save Data */
423    [self savePCConfiguration:thisPC];
424    [table reloadData];
425
426    /* update Table */
427    [self loadConfigurations];
428   
429    /* remove entry from windowMenu */
430    [windowMenu removeItemAtIndex:[windowMenu indexOfItemWithTitle:[NSString stringWithFormat:@"Q - %@", [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]]];
431
432    /* cleanup */
433    [pcsTasks removeObjectForKey:[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]];
434    [thisPC release];
435    [pcsPIDs removeObjectForKey:[NSString stringWithFormat:@"%d", [[aNotification object] processIdentifier]]];
436}
437
438/* control Window */
439- (id) mainWindow {
440//  NSLog(@"cocoaControlController: mainWindow");
441    return mainWindow;
442}
443
444- (void) loadConfigurations
445{
446//  NSLog(@"cocoaControlController: loadConfigurations");
447
448    /* update defaults */
449    userDefaults = [NSUserDefaults standardUserDefaults];
450
451    if (pcs)
452        [pcs release];
453
454    pcs = [[[NSMutableArray alloc] init] retain];
455    NSString *configurationFile;
456    NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtPath:[userDefaults objectForKey:@"dataPath"]];
457    while ((configurationFile = [enumerator nextObject])) {
458        if ([[configurationFile lastPathComponent] isEqualToString:@"configuration.plist"]) {
459            NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", [userDefaults objectForKey:@"dataPath"], configurationFile]];
460            if (data) {
461                NSMutableDictionary *tempPC = [NSPropertyListSerialization
462                    propertyListFromData: data
463                    mutabilityOption: NSPropertyListMutableContainersAndLeaves
464                    format: nil
465                    errorDescription: nil];
466
467                /* upgrade Version 0.1.0.Q */
468                if ([[tempPC objectForKey:@"Version"] isEqual:@"0.1.0.Q"]) {
469                    NSArray *singleArguments = [[NSArray init] arrayWithObjects:@"-snapshot", @"-nographic", @"-audio-help", @"-localtime", @"-full-screen", @"-win2k-hack", @"-usb", @"-s", @"-S", @"-d", @"-std-vga", nil];
470                    NSEnumerator *enumerator = [[tempPC objectForKey:@"Arguments"] keyEnumerator];
471                    id key;
472                    NSMutableString *newArguments = [[NSMutableString alloc] init];
473                    while ((key = [enumerator nextObject])) {
474                        if ([[tempPC objectForKey:@"Arguments"] objectForKey:key]) {
475                            if ([key isEqual:@"-net"] && [[[tempPC objectForKey:@"Arguments"] objectForKey:key] isEqual:@"user"]) {
476                                [newArguments appendFormat:[NSString stringWithFormat:@" -net nic"]];
477                            }
478                            if ([singleArguments containsObject:key]) {
479                                [newArguments appendFormat:[NSString stringWithFormat:@" %@", key]];
480                            } else {
481                                [newArguments appendFormat:[NSString stringWithFormat:@" %@ %@", key, [[tempPC objectForKey:@"Arguments"] objectForKey:key]]];
482                            }
483                        }
484                    }
485                    [tempPC setObject:newArguments forKey:@"Arguments"];
486                    [tempPC setObject:@"0.2.0.Q" forKey:@"Version"];
487                }
488
489                /* isolate Arguments, that we need at hand
490                    -m
491                    -soundhw
492                    -M
493                    -hda
494                    -hdb
495                    -hdd
496                */
497                NSArray *tableArguments = [[NSArray init] arrayWithObjects:@"-m", @"-soundhw", @"-M", @"-hda", @"-hdb", @"-hdd", nil];
498                NSArray *array = [[tempPC objectForKey:@"Arguments"] componentsSeparatedByString:@" "];
499                NSMutableString *option = [[NSMutableString alloc] initWithString:@""];
500                NSMutableString *argument = [[NSMutableString alloc] init];
501                int i;
502                for (i = 1; i < [array count]; i++) {
503                    if ([[array objectAtIndex:i] cString][0] != '-') { //Teil eines Arguments
504                        [argument appendFormat:[NSString stringWithFormat:@" %@", [array objectAtIndex:i]]];
505                    } else {
506                        if ([option length] > 0) {
507                            if ([tableArguments containsObject:option]) {
508                                [[tempPC objectForKey:@"Temporary"] setObject:[argument substringFromIndex:1] forKey:option];
509                            }
510                        }
511                        [option setString:[array objectAtIndex:i]];
512                        [argument setString:@""];
513                    }
514                }
515                if ([tableArguments containsObject:option]) {
516                    [[tempPC objectForKey:@"Temporary"] setObject:[argument substringFromIndex:1] forKey:option];
517                }
518
519                [[tempPC objectForKey:@"Temporary"] setObject:[NSString stringWithFormat:@"%@/%@", [userDefaults objectForKey:@"dataPath"], [configurationFile stringByDeletingLastPathComponent]] forKey:@"-cocoapath"];
520                [pcs addObject: tempPC];
521            }
522        }
523    }
524}
525
526- (void) savePCConfiguration:(id)thisPC
527{
528//  NSLog(@"cocoaControlController: savePCConfiguration");
529
530    NSData *data = [NSPropertyListSerialization
531        dataFromPropertyList: thisPC
532        format: NSPropertyListXMLFormat_v1_0
533        errorDescription: nil];
534    [data writeToFile:[NSString stringWithFormat:@"%@/configuration.plist", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"]] atomically:YES];
535
536}
537
538- (void) updateThumbnails
539{
540//  NSLog(@"cocoaControlController: updateThumbnails");
541   
542    if (pcsImages)
543        [pcsImages release];
544   
545    pcsImages = [[NSMutableArray alloc] init];
546   
547    int i;
548    for (i = 0; i < [pcs count]; i++ ) {
549        NSString *pathImage = [NSString stringWithFormat: @"%@/thumbnail.png", [[[pcs objectAtIndex:i] objectForKey:@"Temporary"] objectForKey:@"-cocoapath"]];
550        NSImage *image =    [[NSImage alloc] initWithContentsOfFile:pathImage];
551        if (image) {
552            [pcsImages addObject:image];
553        } else {
554            [pcsImages addObject:[NSImage imageNamed: @"q_table_shutdown.png"]];
555        }
556        [image release];
557    }
558   
559    [table reloadData];
560}
561
562/* Toolbar delegates */
563- (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted
564{
565    NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdent] autorelease]; 
566    if ([itemIdent isEqual: @"newPCIdentifier"]) {
567        [toolbarItem setLabel: NSLocalizedStringFromTable(@"toolbar:label:newPC", @"Localizable", @"cocoaControlController")];
568        [toolbarItem setPaletteLabel: NSLocalizedStringFromTable(@"toolbar:paletteLabel:newPC", @"Localizable", @"cocoaControlController")];
569        [toolbarItem setToolTip: NSLocalizedStringFromTable(@"toolbar:toolTip:newPC", @"Localizable", @"cocoaControlController")];
570        [toolbarItem setImage: [NSImage imageNamed: @"q_tb_newpc.tiff"]];
571        [toolbarItem setTarget: self];
572        [toolbarItem setAction: @selector( addPC: )];
573    } else if ([itemIdent isEqual: @"editPCIdentifier"]) {
574        [toolbarItem setLabel: NSLocalizedStringFromTable(@"toolbar:label:editPC", @"Localizable", @"cocoaControlController")];
575        [toolbarItem setPaletteLabel: NSLocalizedStringFromTable(@"toolbar:paletteLabel:editPC", @"Localizable", @"cocoaControlController")];
576        [toolbarItem setToolTip: NSLocalizedStringFromTable(@"toolbar:toolTip:editPC", @"Localizable", @"cocoaControlController")];
577        [toolbarItem setImage: [NSImage imageNamed: @"q_tb_editpc.tiff"]];
578        [toolbarItem setTarget: self];
579        [toolbarItem setAction: @selector( editPC: )];
580    } else if([itemIdent isEqual: @"removePCIdentifier"]) {
581        [toolbarItem setLabel: NSLocalizedStringFromTable(@"toolbar:label:removePC", @"Localizable", @"cocoaControlController")];
582        [toolbarItem setPaletteLabel: NSLocalizedStringFromTable(@"toolbar:paletteLabel:removePC", @"Localizable", @"cocoaControlController")];
583        [toolbarItem setToolTip: NSLocalizedStringFromTable(@"toolbar:toolTip:removePC", @"Localizable", @"cocoaControlController")];
584        [toolbarItem setImage: [NSImage imageNamed: @"q_tb_removepc.tiff"]];
585        [toolbarItem setTarget: self];
586        [toolbarItem setAction: @selector( deletePC: )];
587    } else if([itemIdent isEqual: @"startPCIdentifier"]) {
588        [toolbarItem setLabel: NSLocalizedStringFromTable(@"toolbar:label:startPC", @"Localizable", @"cocoaControlController")];
589        [toolbarItem setPaletteLabel: NSLocalizedStringFromTable(@"toolbar:paletteLabel:startPC", @"Localizable", @"cocoaControlController")];
590        [toolbarItem setToolTip: NSLocalizedStringFromTable(@"toolbar:toolTip:startPC", @"Localizable", @"cocoaControlController")];
591        [toolbarItem setImage: [NSImage imageNamed: @"q_tb_startpc.tiff"]];
592        [toolbarItem setTarget: self];
593        [toolbarItem setAction: @selector( tableDoubleClick: )];
594    } else if([itemIdent isEqual: @"importVPC7Identifier"]) {
595        [toolbarItem setLabel: @"Import VPC7"];
596        [toolbarItem setPaletteLabel: @"Import VPC7"];
597        [toolbarItem setToolTip: @"Import VPC7"];
598        [toolbarItem setImage: [NSImage imageNamed: @"q_tb_impvpc.tiff"]];
599        [toolbarItem setTarget: self];
600        [toolbarItem setAction: @selector( importVPC7PC: )];
601    } else if([itemIdent isEqual: @"importQemuXIdentifier"]) {
602        [toolbarItem setLabel: @"Import QemuX"];
603        [toolbarItem setPaletteLabel: @"Import QemuX"];
604        [toolbarItem setToolTip: @"Import QemuX"];
605        [toolbarItem setImage: [NSImage imageNamed: @"q_tb_impqemux.tiff"]];
606        [toolbarItem setTarget: self];
607        [toolbarItem setAction: @selector( importQemuXPCs: )];
608    } else {
609        toolbarItem = nil;
610    }
611   
612    return toolbarItem;
613}
614
615- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
616{
617    return [NSArray arrayWithObjects:
618        @"newPCIdentifier",
619        @"editPCIdentifier",
620        @"removePCIdentifier",
621        @"startPCIdentifier",
622        @"importVPC7Identifier",
623        @"importQemuXIdentifier",
624        NSToolbarCustomizeToolbarItemIdentifier,
625        NSToolbarFlexibleSpaceItemIdentifier,
626        NSToolbarSpaceItemIdentifier,
627        NSToolbarSeparatorItemIdentifier,
628        nil];
629}
630
631- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
632{
633    return [NSArray arrayWithObjects:
634        @"newPCIdentifier",
635//        @"editPCIdentifier",
636//        @"startPCIdentifier",
637//        NSToolbarFlexibleSpaceItemIdentifier,
638//      NSToolbarCustomizeToolbarItemIdentifier,
639//        @"removePCIdentifier",
640        nil];
641}
642
643- (BOOL) validateToolbarItem:(NSToolbarItem *)theItem
644{
645    return YES;
646}
647
648- (void)windowDidBecomeKey:(NSNotification *)aNotification
649{
650//  NSLog(@"windowDidBecomeKey");
651}
652
653-(int)numberOfRowsInTableView:(NSTableView *)table
654{
655    return [pcs count];
656}
657
658-(IBAction) addPC:(id)sender
659{
660//  NSLog(@"cocoaControlController: addPC");
661
662    cocoaControlNewPCAssistant *npa = [[cocoaControlNewPCAssistant alloc] init];
663    [NSBundle loadNibNamed:@"cocoaControlNewPCAssistant" owner:npa];
664    [npa setQSender:self];
665   
666    [NSApp beginSheet:[npa npaPanel]
667        modalForWindow:mainWindow
668        modalDelegate:npa
669        didEndSelector:@selector(npaPanelDidEnd:returnCode:contextInfo:)
670        contextInfo:nil];
671}
672
673- (void) addPCFromDragDrop:(NSString *)path
674{
675//  NSLog(@"cocoaControlController: addPCFromDragDrop");
676
677    cocoaControlNewPCAssistant *npa = [[cocoaControlNewPCAssistant alloc] init];
678    [NSBundle loadNibNamed:@"cocoaControlNewPCAssistant" owner:npa];
679    [npa setQSender:self];
680    [npa setOS:5];
681    [npa setAdditionalHardwarePath: path];
682   
683    [NSApp beginSheet:[npa npaPanel]
684        modalForWindow:mainWindow
685        modalDelegate:npa
686        didEndSelector:@selector(npaPanelDidEnd:returnCode:contextInfo:)
687        contextInfo:nil];
688}
689
690-(IBAction) addPCFromAssistant:(NSMutableDictionary *)thisPC
691{
692//  NSLog(@"cocoaControlController: addPCAssistant");
693
694    /* enter current Values into editPCPanel */
695    [editPC prepareEditPCPanel:thisPC newPC:YES sender:self];
696 
697    /* display editPCPanel */         
698    [[editPC editPCPanel] makeKeyAndOrderFront:self];
699    [NSApp runModalForWindow:[editPC editPCPanel]];
700}
701
702-(void) editThisPC:(id)pc
703{
704//  NSLog(@"cocoaControlController: editThisPC");
705
706    /* enter current Values into editPCPanel */
707    [editPC prepareEditPCPanel:pc newPC:NO sender:self];
708 
709    /* display editPCPanel */
710    [[editPC editPCPanel] makeKeyAndOrderFront:self];
711    [NSApp runModalForWindow:[editPC editPCPanel]];
712}
713
714-(IBAction) editPC:(id)sender
715{
716//  NSLog(@"cocoaControlController: editPC");
717
718    /* no empty line selection */
719    if ( [table numberOfSelectedRows] == 0 )
720        return;
721
722    /* don't allow to edit a running/saved pc */
723    id thisPC;
724    thisPC = [pcs objectAtIndex:[table selectedRow]];
725/*
726    if (![[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"shutdown"]) {
727        [self standardAlert: [NSString stringWithFormat: NSLocalizedStringFromTable(@"editPC:standardAlert", @"Localizable", @"cocoaControlController"),[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]
728             informativeText: [NSString stringWithFormat: NSLocalizedStringFromTable(@"editPC:informativeText", @"Localizable", @"cocoaControlController"), [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]];
729        return;
730    }
731*/
732    [self editThisPC:thisPC];
733}
734
735-(BOOL) checkPC:(id)thisPC name:(NSString *)name create:(BOOL)create
736{
737//  NSLog(@"cocoaControlController: checkPC");
738
739    NSEnumerator *enumerator = [pcs objectEnumerator];
740    id object;
741   
742    if (create) {
743        while ( (object = [enumerator nextObject]) ) {
744            if ([[[object objectForKey:@"PC Data"] objectForKey:@"name"] isEqual: name] )
745                return 0;
746        }
747    } else {
748//      id thisPC = [pcs objectAtIndex:[table selectedRow]];
749        while ( (object = [enumerator nextObject]) ) {
750            if ([[[object objectForKey:@"PC Data"] objectForKey:@"name"] isEqual: name]) {
751                if ( ![[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"] isEqual:name])
752                    return 0;
753            }
754        }
755    }
756   
757    return 1;
758}
759
760- (void) deletePCAlertDidEnd:alert returnCode:(int)returnCode contextInfo:(id)contextInfo
761{
762//  NSLog(@"cocoaControlController: deletePCAlertDidEnd");
763
764    if (returnCode == 1) {
765       
766        /* delete .qvm */
767        NSFileManager *fileManager = [NSFileManager defaultManager];
768        if ([fileManager fileExistsAtPath: [NSString stringWithFormat: @"%@/%@.qvm", [userDefaults objectForKey:@"dataPath"], [[contextInfo objectForKey:@"PC Data"] objectForKey:@"name"]]])
769            [fileManager removeFileAtPath: [NSString stringWithFormat: @"%@/%@.qvm", [userDefaults objectForKey:@"dataPath"], [[contextInfo objectForKey:@"PC Data"] objectForKey:@"name"]] handler:nil];
770   
771        /* cleanup */
772        [self loadConfigurations];
773    }
774}
775
776- (void) deleteThisPC:(id)pc
777{
778//  NSLog(@"cocoaControlController: deleteThisPC");
779
780    /* prepare alert */
781    NSAlert *alert = [NSAlert alertWithMessageText: NSLocalizedStringFromTable(@"deletePC:alertWithMessageText", @"Localizable", @"cocoaControlController")
782                      defaultButton: NSLocalizedStringFromTable(@"deletePC:defaultButton", @"Localizable", @"cocoaControlController")
783                    alternateButton: NSLocalizedStringFromTable(@"deletePC:alternateButton", @"Localizable", @"cocoaControlController")
784                        otherButton:nil
785                  informativeTextWithFormat:[NSString stringWithFormat: NSLocalizedStringFromTable(@"deletePC:informativeTextWithFormat", @"Localizable", @"cocoaControlController"),[[pc objectForKey:@"PC Data"] objectForKey:@"name"]]];
786   
787    /* display alert */
788    [alert beginSheetModalForWindow:mainWindow
789                  modalDelegate:self
790                 didEndSelector:@selector(deletePCAlertDidEnd:returnCode:contextInfo:)
791                 contextInfo:pc];
792}
793
794-(IBAction) deletePC:(id)sender
795{
796//  NSLog(@"cocoaControlController: deletePC");
797
798    /* no empty line selection */
799    if ( [table numberOfSelectedRows] == 0 )
800        return;
801   
802    /* don't allow to delete a running pc */
803    id thisPC;
804    thisPC = [pcs objectAtIndex:[table selectedRow]];
805    if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"running"]) {
806        [self standardAlert: [NSString stringWithFormat: NSLocalizedStringFromTable(@"deletePC:standardAlert", @"Localizable", @"cocoaControlController"),[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]
807             informativeText: [NSString stringWithFormat: NSLocalizedStringFromTable(@"deletePC:informativeText", @"Localizable", @"cocoaControlController"), [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]];
808        return;
809    }
810   
811    [self deleteThisPC:thisPC];
812}
813
814- (BOOL) importFreeOSZooPC:(NSString *)name withPath:(NSString *)path
815{
816//  NSLog(@"cocoaControlController: importFreeOSZooPC");
817    if([path isEqualTo: [userDefaults objectForKey: @"dataPath"]]) {
818        // Is considered to be a qvm which was extracted directly to the dataPath directory
819        [self loadConfigurations];
820        return YES;
821    }
822       
823    NSMutableDictionary * thisPC = [[[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:
824        [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:@"Q", @"none", [NSDate date], @"Q guest PC from FreeOSZoo", nil] forKeys:[NSArray arrayWithObjects: @"Author", @"Copyright", @"Date", @"Description", nil]],
825        [[NSMutableString alloc] initWithString:@" -m 128 -net user -boot c -localtime -smb ~/Desktop/Q Shared Files/"],
826        [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:@"My new PC", @"shutdown", @"x86", nil] forKeys:[NSArray arrayWithObjects: @"name", @"state", @"architecture", nil]],
827        [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithBool:true], nil] forKeys:[NSArray arrayWithObjects: @"QWinDrivers", nil]],
828        @"0.2.0.Q",
829        nil
830    ] forKeys:[NSArray arrayWithObjects:@"About", @"Arguments", @"PC Data", @"Temporary", @"Version", nil]] retain];
831   
832    [[thisPC objectForKey:@"Temporary"] setObject:path forKey:@"-cocoapath"];
833#if kju_debug
834    NSLog(@"-cocoapath: %@", path);
835#endif
836    [[thisPC objectForKey:@"PC Data"] setObject:name forKey:@"name"];
837   
838    // TODO: use README file to get HD and other arguments
839    // for now we search for a .img/.qcow file and use it as HD
840    BOOL foundHD = NO;
841    BOOL foundDir = NO;
842    BOOL foundReadme = NO;
843    NSFileManager * manager = [NSFileManager defaultManager];
844    NSArray * dirContents = [manager directoryContentsAtPath:path];
845    NSArray * subDir;
846    int i,ii,j,k;
847    for (i=0; i<=[dirContents count]-1; i++) {
848        if (([[[dirContents objectAtIndex:i] pathExtension] isEqualToString:@"img"] || [[[dirContents objectAtIndex:i] pathExtension] isEqualToString:@"qcow"] || [[[dirContents objectAtIndex:i] pathExtension] isEqualToString:@"dsk"]) && (![[[manager fileAttributesAtPath:[path stringByAppendingPathComponent:[dirContents objectAtIndex:i]] traverseLink:NO] objectForKey:NSFileType] isEqualTo:NSFileTypeDirectory])) {
849            foundHD = YES;
850            break;
851        } else if([[[manager fileAttributesAtPath:[path stringByAppendingPathComponent:[dirContents objectAtIndex:i]] traverseLink:NO] objectForKey:NSFileType] isEqualTo:NSFileTypeDirectory]) {
852            foundDir = YES;
853            //NSLog(@"Found Dir: %@", [dirContents objectAtIndex:i]);
854            break;
855        } else {
856            //NSLog(@"Found no image or folder.");
857        }
858    }
859
860    if(foundHD) {
861       //if we found the hd image in the root folder, we only need to append -hda file to arguments
862       [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -hda %@", [dirContents objectAtIndex:i]]];
863    } else if(foundDir) {
864       // if we found a folder, the hd should be in it
865       // if hd is found, move all files in the folder to root directory and delete folder
866       subDir = [manager directoryContentsAtPath:[path stringByAppendingPathComponent:[dirContents objectAtIndex:i]]];
867       for(ii=0; i<=[subDir count]-1; ii++) {
868            // search for .img or .qcow
869            if([[[subDir objectAtIndex:ii] pathExtension] isEqualToString:@"img"] || [[[subDir objectAtIndex:i] pathExtension] isEqualToString:@"qcow"]) {
870                //NSLog(@"found HD in subdir!");
871                // move all files to root dir and delete the folder
872                for(j=0; j<[subDir count]; j++) {
873                    [manager movePath:[path stringByAppendingPathComponent:[[dirContents objectAtIndex:i] stringByAppendingPathComponent:[subDir objectAtIndex:j]]] toPath:[path stringByAppendingPathComponent:[subDir objectAtIndex:j]] handler:nil];
874                }
875                [manager removeFileAtPath:[path stringByAppendingPathComponent:[dirContents objectAtIndex:i]] handler:nil];
876                // append hd name to arguments
877                [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -hda %@", [subDir objectAtIndex:ii]]];
878                foundHD = YES;
879                break;
880            }
881        }
882    }
883   
884    // search for a readme file, if found, open it with TextEdit
885    dirContents = [manager directoryContentsAtPath:path];
886    for(k=0; k<[dirContents count]; k++) {
887       if([[dirContents objectAtIndex:k] isEqualToString:@"README"]) {
888           foundReadme = YES;
889           break;
890       }
891    }
892   
893    /* save Configuration */
894    [self savePCConfiguration:thisPC];
895   
896    /* update Table */
897    [self loadConfigurations];
898   
899    /* open Readme */
900    if(foundReadme) [[NSWorkspace sharedWorkspace] openFile:[path stringByAppendingPathComponent:@"README"] withApplication:@"TextEdit.app"];
901   
902    return foundHD;
903}
904
905- (NSString *) convertDI:(NSString *)oldImagePath to:(NSString *)newPCname
906{
907//  NSLog(@"cocoaControlController: convertDI");
908
909    /* search a free Name */
910    int i = 1;
911    NSString *name;
912    NSString *path = [NSString stringWithString:[[NSString stringWithFormat:@"%@/%@.qvm",[userDefaults objectForKey:@"dataPath"], newPCname] stringByExpandingTildeInPath]];
913    NSFileManager *fileManager = [NSFileManager defaultManager];
914
915    while ([fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@", path, [NSString stringWithFormat:@"Harddisk_%d.qcow2", i]]])
916        i++;
917    name = [NSString stringWithFormat:@"Harddisk_%d.qcow2", i];
918   
919    /* convert diskImage */
920    NSArray *arguments = [NSArray arrayWithObjects:@"convert", @"-c", @"-O", @"qcow2", oldImagePath, [NSString stringWithFormat:@"%@/%@", path, name], nil];
921    NSTask *task;
922    task = [[NSTask alloc] init];
923    [task setLaunchPath: [NSString stringWithFormat:@"%@/MacOS/qemu-img", [[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent]]];
924    [task setArguments: arguments];
925    [task launch];
926    [task waitUntilExit];
927    int status = [task terminationStatus];
928    [task release];
929   
930    if (status == 0) {
931        return name;
932    }
933   
934    return [NSString stringWithString:@""];
935}
936
937- (void) importVPC7PCDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(id)contextInfo
938{
939//  NSLog(@"cocoaControlController: importVPC7PCDidEnd");
940
941    /* hide Save Sheet */
942    [ sheet orderOut:self ];
943   
944    if ( returnCode == NSOKButton ) {
945        NSString *tempString;
946        NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/Configuration.plist", [sheet filename]]];
947        if (!data) {
948            [self standardAlert: NSLocalizedStringFromTable(@"importVPC7PC:standardAlert:plist", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importVPC7PC:informativeText:plist", @"Localizable", @"cocoaControlController")];
949            return;
950        }
951       
952        /* setup & show Progress panel */
953        [progressTitle setStringValue: NSLocalizedStringFromTable(@"importVPC7PC:progress:title", @"Localizable", @"cocoaControlController")];
954        [progressText setStringValue:[[[sheet filename] lastPathComponent] stringByDeletingPathExtension]];
955        [progressStatusText setStringValue: NSLocalizedStringFromTable(@"importVPC7PC:progress:config", @"Localizable", @"cocoaControlController")];
956        [progressIndicator setUsesThreadedAnimation:TRUE];
957        [progressIndicator setIndeterminate:FALSE];
958        [progressIndicator setMaxValue:100];
959        [progressIndicator setDoubleValue:10];
960       
961        [NSApp beginSheet:progressPanel
962        modalForWindow:mainWindow
963        modalDelegate:nil
964        didEndSelector:nil
965        contextInfo:nil];
966       
967        NSDictionary *vpc7 = [NSPropertyListSerialization
968            propertyListFromData: data
969            mutabilityOption: nil
970            format: nil
971            errorDescription: nil];
972
973        /* check if guest is Shutdown */
974        if (![vpc7 objectForKey:@"Running At Quit"]) {
975            [NSApp endSheet:progressPanel];
976            [progressPanel orderOut:self];
977            [self standardAlert: NSLocalizedStringFromTable(@"importVPC7PC:standardAlert:running", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importVPC7PC:informativeText:running", @"Localizable", @"cocoaControlController")];
978            return;
979        }
980       
981        /* search a free Name */
982        int i = 2;
983        NSString *name;
984        NSFileManager *fileManager = [NSFileManager defaultManager];
985        if ([fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@.qvm",[userDefaults objectForKey:@"dataPath"], [[[sheet filename] lastPathComponent] stringByDeletingPathExtension]]]) {
986            while ([fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@_%d.qvm",[userDefaults objectForKey:@"dataPath"], [[[sheet filename] lastPathComponent] stringByDeletingPathExtension], i]])
987                i++;       
988            name = [NSString stringWithFormat:@"%@_%d",[[[sheet filename] lastPathComponent] stringByDeletingPathExtension], i];
989        } else {
990            name = [[[sheet filename] lastPathComponent] stringByDeletingPathExtension];
991        }
992
993        /* standard values */
994        NSMutableDictionary *thisPC = [[[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:
995        [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:@"Q", @"none", [NSDate date], @"Q guest PC converted from vpc7.", nil] forKeys:[NSArray arrayWithObjects: @"Author", @"Copyright", @"Date", @"Description", nil]],
996        [[NSMutableString alloc] initWithString:@"-net nic -net user -boot c -localtime"],
997        [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:name, @"shutdown", @"x86", nil] forKeys:[NSArray arrayWithObjects: @"name", @"state", @"architecture", nil]],
998        [[NSMutableDictionary alloc] init],
999        @"0.2.0.Q",
1000        nil
1001        ] forKeys:[NSArray arrayWithObjects:@"About", @"Arguments", @"PC Data", @"Temporary", @"Version", nil]] retain];
1002                           
1003        [[thisPC objectForKey:@"Temporary"] setObject:[NSString stringWithFormat:@"%@/%@.qvm", [userDefaults objectForKey:@"dataPath"], name] forKey:@"-cocoapath"];
1004       
1005        /* vpc7 values */
1006        /* RAM */
1007        [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -m %@", [vpc7 objectForKey:@"Memory/RAM Size"]]];
1008
1009        /* CD-ROM */
1010        if ([vpc7 objectForKey:@"CDROM/Captured Image Location"]) {
1011            [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -cdrom %@", [[vpc7 objectForKey:@"CDROM/Captured Image Location"] objectForKey:@"_CFURLString"]]];
1012        } else {
1013            [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -cdrom /dev/cdrom"]];
1014        }
1015       
1016        [progressIndicator setDoubleValue:20];
1017
1018        /* Harddisks */
1019        NSArray *ideChannels = [NSArray arrayWithObjects:@"-hda",@"-hdb",@"-hdd",nil];
1020        if ([fileManager fileExistsAtPath:[[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"]] == NO)
1021            [fileManager createDirectoryAtPath:[[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"] attributes: nil];   
1022        i = 0;
1023        while (([vpc7 objectForKey:[NSString stringWithFormat:@"IDE/Drive/%D/Location",i]]) && (i < 3)) {
1024            [progressStatusText setStringValue:[NSString stringWithFormat: NSLocalizedStringFromTable(@"importVPC7PC:progress:hdx", @"Localizable", @"cocoaControlController"), i]];
1025            /* Try relative Path */
1026            if ([fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@/%@/BaseDrive.vhd", [sheet filename], [[[vpc7 objectForKey:[NSString stringWithFormat:@"IDE/Drive/%D/Location",i]] objectForKey:@"_CFURLString"] lastPathComponent]]]) {
1027                tempString = [self convertDI:[NSString stringWithFormat:@"%@/%@/BaseDrive.vhd", [sheet filename], [[[vpc7 objectForKey:[NSString stringWithFormat:@"IDE/Drive/%D/Location",i]] objectForKey:@"_CFURLString"] lastPathComponent]] to:name];
1028                if ([tempString isEqual:@""]) {
1029                    [NSApp endSheet:progressPanel];
1030                    [progressPanel orderOut:self];
1031                    [self standardAlert: NSLocalizedStringFromTable(@"importVPC7PC:standardAlert:imageConvert", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importVPC7PC:informativeText:imageConvert", @"Localizable", @"cocoaControlController")];
1032                    return;     
1033                }
1034                [[thisPC objectForKey:@"Arguments"] appendFormat: [NSString stringWithFormat:@" %@ %@", [ideChannels objectAtIndex:i], tempString]];
1035            /* Try absolute Path */
1036            } else if ([fileManager fileExistsAtPath:[NSString stringWithFormat:@"%@/BaseDrive.vhd", [[vpc7 objectForKey:[NSString stringWithFormat:@"IDE/Drive/%D/Location",i]] objectForKey:@"_CFURLString"]]]) {
1037                tempString = [self convertDI:[NSString stringWithFormat:@"%@/BaseDrive.vhd", [[vpc7 objectForKey:[NSString stringWithFormat:@"IDE/Drive/%D/Location",i]] objectForKey:@"_CFURLString"]] to:name];
1038                if ([tempString isEqual:@""]) {
1039                    [NSApp endSheet:progressPanel];
1040                    [progressPanel orderOut:self];
1041                    [self standardAlert: NSLocalizedStringFromTable(@"importVPC7PC:standardAlert:imageConvert", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importVPC7PC:informativeText:imageConvert", @"Localizable", @"cocoaControlController")];
1042                    return;     
1043                }
1044                [[thisPC objectForKey:@"Arguments"] appendFormat: [NSString stringWithFormat:@" %@ %@", [ideChannels objectAtIndex:i], tempString]];
1045            }
1046            i++;
1047            [progressIndicator setDoubleValue:(20 + i * 20)];
1048        }
1049       
1050        /* save Configuration */
1051        [self savePCConfiguration:thisPC];
1052        [progressIndicator setDoubleValue:100];
1053
1054        /* update Table */
1055        [self loadConfigurations];
1056
1057        /* hide panel */
1058        [NSApp endSheet:progressPanel];
1059        [progressPanel orderOut:self];
1060       
1061        /* show warning */
1062        [self standardAlert: NSLocalizedStringFromTable(@"importVPC7PC:standardAlert:finish", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importVPC7PC:informativeText:finish", @"Localizable", @"cocoaControlController")];
1063    }
1064}
1065
1066- (IBAction) importVPC7PC:(id)sender
1067{
1068//  NSLog(@"cocoaControlController: importVPC7PC");
1069
1070    NSOpenPanel *openPanel = [[NSOpenPanel alloc] init];
1071    [openPanel setCanChooseDirectories:YES];
1072    [openPanel setCanCreateDirectories:NO];
1073    [openPanel setCanChooseFiles:YES];
1074    [openPanel beginSheetForDirectory:[NSString stringWithFormat:@"%@/Documents/Virtual PC List", NSHomeDirectory()]
1075        file:nil
1076        types:[NSArray arrayWithObjects:@"vpc7", nil]
1077        modalForWindow:mainWindow
1078        modalDelegate:self
1079        didEndSelector:@selector(importVPC7PCDidEnd:returnCode:contextInfo:)
1080        contextInfo:sender];
1081}
1082
1083- (IBAction) importQemuXPCs:(id)sender
1084{
1085//  NSLog(@"cocoaControlController: importQemuXPCs");
1086
1087    /* setup & show Progress panel */
1088    [progressTitle setStringValue: NSLocalizedStringFromTable(@"importQemuXPCs:progress:title", @"Localizable", @"cocoaControlController")];
1089    [progressText setStringValue:@""];
1090    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"importQemuXPCs:progress:config", @"Localizable", @"cocoaControlController")];
1091    [progressIndicator setUsesThreadedAnimation:TRUE];
1092    [progressIndicator setIndeterminate:FALSE];
1093    [progressIndicator setMaxValue:100];
1094    [progressIndicator setDoubleValue:0];
1095       
1096    [NSApp beginSheet:progressPanel
1097        modalForWindow:mainWindow
1098        modalDelegate:nil
1099        didEndSelector:nil
1100        contextInfo:nil];
1101
1102//  NSMutableString *message = [NSMutableString stringWithFormat: NSLocalizedStringFromTable(@"importQemuXPCs:message", @"Localizable", @"cocoaControlController")];
1103    NSArray * qemux = [NSArray arrayWithContentsOfFile:[@"~/Library/Application Support/QemuX/oslist.plist" stringByExpandingTildeInPath]];
1104
1105    if (!qemux) {
1106        [NSApp endSheet:progressPanel];
1107        [progressPanel orderOut:self];
1108        [self standardAlert: NSLocalizedStringFromTable(@"importQemuXPCs:standardAlert:plist", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importQemuXPCs:informativeText:plist", @"Localizable", @"cocoaControlController")];
1109        return;
1110    }
1111
1112    /* check if guest is Shutdown, seems difficult for QemuX
1113    should we instead check for QemuX running?
1114    if (![vpc7 objectForKey:@"Running At Quit"]) {
1115        [self standardAlert:@"Import of VPC7 Guest" informativeText:@"PC is running! It must be shut down before it can be converted."];
1116        return;
1117    }
1118    */
1119   
1120    /* standard values, no -boot c here, we look for the boot param later */
1121    NSMutableDictionary *thisPC = [[[NSMutableDictionary alloc] init] retain];
1122    NSDictionary *standardValues = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects:
1123    [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:@"Q", @"none", [NSDate date], @"Q guest PC converted from QemuX.", nil] forKeys:[NSArray arrayWithObjects: @"Author", @"Copyright", @"Date", @"Description", nil]],
1124    [[NSMutableString alloc] initWithString:@"-net nic -net user -localtime"],
1125    [[NSMutableDictionary alloc] initWithObjects:[NSArray arrayWithObjects:@"name_placeholder", @"shutdown", @"x86", nil] forKeys:[NSArray arrayWithObjects: @"name", @"state", @"architecture", nil]],
1126    [[NSMutableDictionary alloc] init],
1127    @"0.2.0.Q",
1128    nil
1129    ] forKeys:[NSArray arrayWithObjects:@"About", @"Arguments", @"PC Data", @"Temporary", @"Version", nil]];
1130   
1131    int i;
1132    for(i = 0; i<=[qemux count]-1; i++) {
1133        /* go through the array and import the pc's */
1134       
1135        NSFileManager * fileManager = [NSFileManager defaultManager];
1136        /* set Standard Values & Name */
1137        NSString *name = [[qemux objectAtIndex:i] objectForKey:@"name"];
1138        /* just to be sure... */
1139        [thisPC removeAllObjects];
1140        [thisPC setDictionary:standardValues];
1141        [[thisPC objectForKey:@"PC Data"] setObject:name forKey:@"name"];
1142//      [message appendFormat:@"%@\n", name];
1143
1144        /* update Progresspanel text */
1145        [progressText setStringValue:[NSString stringWithFormat: NSLocalizedStringFromTable(@"importQemuXPCs:progress:pc", @"Localizable", @"cocoaControlController"), name]];
1146
1147       
1148        /* set the -cocoapath */
1149        [[thisPC objectForKey:@"Temporary"] setObject:[NSString stringWithFormat:@"%@/%@.qvm", [userDefaults objectForKey:@"dataPath"], name] forKey:@"-cocoapath"];
1150#if kju_debug
1151        NSLog(@"cocoapath: %@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"]);
1152#endif
1153       
1154        /* Create .qvm */
1155        [fileManager createDirectoryAtPath:[[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"] attributes: nil];
1156       
1157        /* QemuX values */
1158        /* RAM */
1159        [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -m %@", [[qemux objectAtIndex:i] objectForKey:@"ram"]]];
1160       
1161        /* CD-Rom */
1162        if([[[qemux objectAtIndex:i] objectForKey:@"cdrom"] isEqualToString:@""]) {
1163            /* when cd-rom is not given, add /dev/cdrom */
1164            [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -cdrom /dev/cdrom"]];
1165        } else {
1166            /* path to cd-image given, save it and copy image */
1167            [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -cdrom %@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"] ,[[[qemux objectAtIndex:i] objectForKey:@"cdrom"] lastPathComponent]]];
1168            [fileManager copyPath:[[qemux objectAtIndex:i] objectForKey:@"cdrom"] toPath:[NSString stringWithFormat:@"%@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:@"cdrom"] lastPathComponent]] handler: nil];
1169        }
1170       
1171        /* Harddisks */
1172        NSArray * hds = [NSArray arrayWithObjects:@"fda",@"hda",@"hdb",@"hdc",@"hdd", nil];
1173       
1174        int ii;
1175        for(ii = 0; ii<=[hds count]-1; ii++) {
1176            [progressStatusText setStringValue:[NSString stringWithFormat: NSLocalizedStringFromTable(@"importQemuXPCs:progress:hdx", @"Localizable", @"cocoaControlController"), [[[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] lastPathComponent]]];
1177            if([[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] && ![[[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] isEqualToString:@""]) {
1178                // add to arguments
1179                [[thisPC objectForKey:@"Arguments"] appendFormat:[NSString stringWithFormat:@" -%@ %@",[hds objectAtIndex:ii], [[[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] lastPathComponent]]];
1180                // copy over into .qvm
1181                [fileManager copyPath:[[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] toPath:[NSString stringWithFormat:@"%@/%@",  [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] lastPathComponent]] handler:nil];
1182#if kju_debug
1183                NSLog(@"copy allowed, done.");
1184#endif
1185            }
1186#if kju_debug
1187            NSLog(@"hd: %@", [[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]]);
1188            NSLog(@"copy from %@ to %@", [[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]], [NSString stringWithFormat:@"%@/%@",    [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:[hds objectAtIndex:ii]] lastPathComponent]]);
1189#endif
1190        }
1191       
1192        // Boot param
1193        switch ([[[qemux objectAtIndex:i] objectForKey:@"boot"] intValue]) {
1194            case 0:
1195                [[thisPC objectForKey:@"Arguments"] appendString:@" -boot a"];
1196                break;
1197            case 1:
1198                [[thisPC objectForKey:@"Arguments"] appendString:@" -boot c"];
1199                break;
1200            case 2:
1201                [[thisPC objectForKey:@"Arguments"] appendString:@" -boot d"];
1202                break;
1203            default:
1204                [[thisPC objectForKey:@"Arguments"] appendString:@" -boot c"];
1205        }
1206       
1207        /* additional options */
1208        // Linux boot specific:
1209       
1210        // Linux Kernel
1211        if(([[qemux objectAtIndex:i] objectForKey:@"linuxKernel"]) && !([[[qemux objectAtIndex:i] objectForKey:@"linuxKernel"] isEqualToString:@""])) {
1212            // copy into .qvm, set the parameter
1213            [fileManager copyPath:[[qemux objectAtIndex:i] objectForKey:@"linuxKernel"] toPath:[NSString stringWithFormat:@"%@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:@"linuxKernel"] lastPathComponent]] handler:nil];     
1214            [[thisPC objectForKey:@"Arguments"] appendFormat:@" -kernel %@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:@"linuxKernel"] lastPathComponent]];                     
1215        }
1216       
1217        // Linux Ramdisk
1218        if(([[qemux objectAtIndex:i] objectForKey:@"linuxRamdisk"]) && !([[[qemux objectAtIndex:i] objectForKey:@"linuxRamdisk"] isEqualToString: @""])) {
1219            // copy into .qvm, set the parameter
1220            [fileManager copyPath:[[qemux objectAtIndex:i] objectForKey:@"linuxRamdisk"] toPath:[NSString stringWithFormat:@"%@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:@"linuxRamdisk"] lastPathComponent]] handler:nil];
1221            [[thisPC objectForKey:@"Arguments"] appendFormat:@" -initrd %@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], [[[qemux objectAtIndex:i] objectForKey:@"linuxRamdisk"] lastPathComponent]];
1222        }
1223       
1224        // Linux Kernel Command Line
1225        if(([[qemux objectAtIndex:i] objectForKey:@"linuxCmdline"]) && !([[[qemux objectAtIndex:i] objectForKey:@"linuxCmdline"] isEqualToString: @""])) {
1226            // set the parameter     
1227            [[thisPC objectForKey:@"Arguments"] appendFormat:@" -append %@", [[qemux objectAtIndex:i] objectForKey:@"linuxCmdline"]];
1228        }
1229       
1230        // additional options, summarized
1231        // options with integer values 0,1 (NO,YES)
1232        NSArray * additionalIntSynonym = [NSArray arrayWithObjects:@"freeze",@"gdb",@"nographic",@"snapshot",@"stdvga",nil];
1233        // array for the QEMU command line paramters from above, because QemuX used to store them with slightly different names
1234        NSArray * additionalIntQemu = [NSArray arrayWithObjects:@"-S",@"-s",@"-nographic",@"-snapshot",@"-std-vga",nil];
1235       
1236        for(ii=0; ii<=[additionalIntSynonym count]-1; ii++) {
1237            // if they are TRUE, append to arguments
1238            if([[[qemux objectAtIndex:i] objectForKey:[additionalIntSynonym objectAtIndex:ii]] intValue] == 1) {
1239                [[thisPC objectForKey:@"Arguments"] appendFormat:@" %@", [additionalIntQemu objectAtIndex:ii]];
1240            }
1241        }
1242       
1243        // options with string values
1244        NSArray * additionalStringSynonym = [NSArray arrayWithObjects:@"gdbport",@"monitor",@"redir",@"serial",nil];
1245        NSArray * additionalStringQemu = [NSArray arrayWithObjects:@"-p",@"-monitor",@"-redir",@"serial",nil];
1246
1247        for(ii=0; ii<=[additionalStringSynonym count]-1; ii++) {
1248            // if they are not empty, append to arguments
1249            if(![[[qemux objectAtIndex:i] objectForKey:[additionalStringSynonym objectAtIndex:ii]] isEqualToString:@""]) {
1250                [[thisPC objectForKey:@"Arguments"] appendFormat:@" %@ %@", [additionalStringQemu objectAtIndex:ii], [[qemux objectAtIndex:i] objectForKey:[additionalStringSynonym objectAtIndex:ii]]];
1251            }
1252        }
1253           
1254        /* save Configuration */
1255        [self savePCConfiguration:thisPC];
1256       
1257        /* update Progressbar */
1258        [progressIndicator setDoubleValue:(100 / [qemux count] * (i + 1))];
1259    }
1260   
1261//  [message appendString: NSLocalizedStringFromTable(@"importQemuXPCs:message:appendString", @"Localizable", @"cocoaControlController")];
1262
1263    /* update Table */
1264    [self loadConfigurations];
1265
1266    /* hide panel */
1267    [NSApp endSheet:progressPanel];
1268    [progressPanel orderOut:self];
1269   
1270    /* show warning */
1271    [self standardAlert: NSLocalizedStringFromTable(@"importQemuXPCs:standardAlert:finish", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importQemuXPCs:informativeText:finish", @"Localizable", @"cocoaControlController")];
1272}
1273
1274- (void) updatePC:(id)thisPC
1275{
1276//  NSLog(@"cocoaControlController: updatePC");
1277
1278    int i;
1279    NSString *tempString;
1280    NSString *harddisk;
1281    NSString *path;
1282    NSString *vmPath;
1283
1284    NSArray *harddisks = [NSArray arrayWithObjects:@"-hda",@"-hdb",@"-hdd",nil];
1285    NSFileManager *fileManager = [NSFileManager defaultManager];
1286
1287    /* setup & show Progress panel */
1288    [progressTitle setStringValue: NSLocalizedStringFromTable(@"updatePC:progress:title", @"Localizable", @"cocoaControlController")];
1289    [progressText setStringValue: NSLocalizedStringFromTable(@"updatePC:progress:text", @"Localizable", @"cocoaControlController")];
1290    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"updatePC:progress:config", @"Localizable", @"cocoaControlController")];
1291    [progressIndicator setUsesThreadedAnimation:TRUE];
1292    [progressIndicator setIndeterminate:TRUE];
1293    [progressIndicator startAnimation:self];
1294
1295    [NSApp beginSheet:progressPanel
1296        modalForWindow:mainWindow
1297        modalDelegate:nil
1298        didEndSelector:nil
1299        contextInfo:nil];
1300
1301    for (i = 0; i < [harddisks count]; i++) {
1302        if ((harddisk = [[thisPC objectForKey:@"Temporary"] objectForKey:[harddisks objectAtIndex:i]])) {
1303
1304            //path
1305            if (![harddisk isAbsolutePath]) { //we only convert files inside the .qvm package
1306                path = [NSString stringWithFormat:@"%@/%@",[[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], harddisk];
1307
1308                //convert
1309                tempString = [self convertDI:path to:[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]];
1310                if ([tempString isEqual:@""]) {
1311                    [NSApp endSheet:progressPanel];
1312                    [progressPanel orderOut:self];
1313                    [self standardAlert: NSLocalizedStringFromTable(@"updatePC:standardAlert:imageConvert", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"updatePC:informativeText:imageConvert", @"Localizable", @"cocoaControlController")];
1314                    return;
1315                }
1316
1317                //delete old Harddisk files
1318                [fileManager removeFileAtPath:path handler:nil];
1319
1320                //delete old VM states
1321                vmPath = [NSString stringWithFormat:@"%@/saved.vm", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"]];
1322                if ([fileManager fileExistsAtPath:vmPath])
1323                    [fileManager removeFileAtPath:vmPath handler:nil];
1324
1325                //rename or move new File (we only want .qcow2)
1326                if ([[harddisk pathExtension] isEqual:@"qcow2"]) {
1327                    [fileManager movePath:[NSString stringWithFormat:@"%@/%@", [[thisPC objectForKey:@"Temporary"] objectForKey:@"-cocoapath"], tempString] toPath:path handler:nil];
1328                } else {
1329                    NSRange range =[[thisPC objectForKey:@"Arguments"] rangeOfString:harddisk];
1330                    [thisPC setObject:[NSString stringWithFormat:@"%@%@%@",[[thisPC objectForKey:@"Arguments"] substringToIndex:range.location], tempString, [[thisPC objectForKey:@"Arguments"] substringFromIndex:(range.location + range.length)]] forKey:@"Arguments"];
1331                }
1332
1333            }
1334
1335        }
1336    }
1337
1338    /* save Configuration */
1339    [self savePCConfiguration:thisPC];
1340    [progressIndicator stopAnimation:self];
1341
1342    /* update Table */
1343    [self loadConfigurations];
1344
1345    /* hide panel */
1346    [NSApp endSheet:progressPanel];
1347    [progressPanel orderOut:self];
1348
1349//    /* show warning */
1350//    [self standardAlert: NSLocalizedStringFromTable(@"updatePC:standardAlert:finish", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"updatePC:informativeText:finish", @"Localizable", @"cocoaControlController")];
1351}
1352
1353- (void) updatePCAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(id)contextInfo
1354{
1355//  NSLog(@"cocoaControlController: updatePCAlertDidEnd");
1356   
1357    [[alert window] orderOut:self];
1358
1359    if(returnCode == NSOKButton)
1360       [self updatePC:contextInfo];
1361}
1362
1363- (IBAction) updateThisPC:(id)sender
1364{
1365//  NSLog(@"cocoaControlController: updateThisPC");
1366
1367    /* no empty line selection */
1368    if ( [table numberOfSelectedRows] == 0 )
1369        return;
1370   
1371    /* don't allow to export a running/saved pc */
1372    id thisPC;
1373    thisPC = [pcs objectAtIndex:[table selectedRow]];
1374   
1375    if (![[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"shutdown"]) {
1376        [self standardAlert: [NSString stringWithFormat: NSLocalizedStringFromTable(@"updateThisPC:standardAlert", @"Localizable", @"cocoaControlController"),[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]
1377             informativeText: [NSString stringWithFormat: NSLocalizedStringFromTable(@"updateThisPC:informativeText", @"Localizable", @"cocoaControlController"), [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]];
1378        return;
1379    }
1380   
1381    /* prepare informative alert */
1382    NSAlert *alert = [NSAlert alertWithMessageText: [NSString stringWithFormat: NSLocalizedStringFromTable(@"updateThisPC:alertWithMessageText", @"Localizable", @"cocoaControlController"), [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]
1383                      defaultButton: NSLocalizedStringFromTable(@"updateThisPC:defaultButton", @"Localizable", @"cocoaControlController")
1384                    alternateButton:NSLocalizedStringFromTable(@"updateThisPC:alternateButton", @"Localizable", @"cocoaControlController")
1385                        otherButton:nil
1386                  informativeTextWithFormat: NSLocalizedStringFromTable(@"updateThisPC:informativeTextWithFormat", @"Localizable", @"cocoaControlController")];
1387   
1388    // display alert
1389    [alert beginSheetModalForWindow:mainWindow
1390                  modalDelegate:self
1391                 didEndSelector:@selector(updatePCAlertDidEnd:returnCode:contextInfo:)
1392                 contextInfo:thisPC];
1393}
1394
1395-(void) exportPCToFlashDrive:(id)pc
1396{
1397//  NSLog(@"cocoaControlController: exportThisPCToFlashDrive");
1398    // get the export path
1399    int result;
1400    NSSavePanel *savePanel = [[NSSavePanel alloc] init];
1401    [savePanel setCanCreateDirectories:YES];
1402    [savePanel setTitle: NSLocalizedStringFromTable(@"exportPCToFlashDrive:savePanel:title", @"Localizable", @"cocoaControlController")];
1403    [savePanel setPrompt: NSLocalizedStringFromTable(@"exportPCToFlashDrive:savePanel:prompt", @"Localizable", @"cocoaControlController")];
1404    result = [savePanel runModalForDirectory: NSOpenStepRootDirectory()
1405        file:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]];
1406   
1407    if(result != NSOKButton)
1408        return;
1409   
1410    NSString * exportPath = [savePanel filename];
1411    NSString * pkgSrcPath = [NSString stringWithFormat:@"%@/%@.qvm", [userDefaults objectForKey:@"dataPath"], [[pc objectForKey:@"PC Data"] objectForKey:@"name"]];
1412   
1413    /* 0. setup & show Progress panel */
1414    [progressTitle setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:title", @"Localizable", @"cocoaControlController")];
1415    [progressText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:text", @"Localizable", @"cocoaControlController")];
1416    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:statusText1", @"Localizable", @"cocoaControlController")];
1417    [progressIndicator setUsesThreadedAnimation:YES];
1418    [progressIndicator setIndeterminate:NO];
1419    [progressIndicator setMaxValue:100];
1420    [progressIndicator setDoubleValue:10];
1421    [progressIndicator startAnimation:self];
1422       
1423    [NSApp beginSheet:progressPanel
1424        modalForWindow:mainWindow
1425        modalDelegate:nil
1426        didEndSelector:nil
1427        contextInfo:nil];
1428
1429    NSFileManager * fileManager = [NSFileManager defaultManager];
1430    /* 1. get architecture and copy binary.app */
1431    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:statusText2", @"Localizable", @"cocoaControlController")];
1432    NSString * srcPath = [NSString stringWithFormat:@"%@/Contents/MacOS/%@.app", [[NSBundle mainBundle] bundlePath], [cpuTypes objectForKey:[[pc objectForKey:@"PC Data"] objectForKey:@"architecture"]]];
1433    NSString * destPath = [NSString stringWithFormat:@"%@.app", exportPath];
1434
1435    [fileManager copyPath: srcPath toPath: destPath handler: nil];
1436    /* 1.2 copy q_icon_portable.icns to binary package */
1437    [fileManager removeFileAtPath: [destPath stringByAppendingPathComponent:@"Contents/Resources/q_icon.icns"] handler: nil];
1438    [fileManager copyPath: [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"q_icon_portable.icns"] toPath: [destPath stringByAppendingPathComponent:@"Contents/Resources/q_icon.icns"] handler: nil];
1439   
1440    /* 1.3 set NSUIElement to 0 to have a Dock Icon for the Exported Guest PC */
1441    NSMutableDictionary * appDict = [NSMutableDictionary dictionaryWithContentsOfFile: [destPath stringByAppendingPathComponent: @"Contents/Info.plist"]];
1442    [appDict setObject:[NSNumber numberWithInt:0] forKey:@"NSUIElement"];
1443    [appDict writeToFile:[destPath stringByAppendingPathComponent: @"Contents/Info.plist"] atomically: YES];
1444   
1445    [progressIndicator setDoubleValue:20];
1446
1447    /* 2. create Guest folder in binary package & copy qvm package */
1448    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:statusText3", @"Localizable", @"cocoaControlController")];
1449    [fileManager createDirectoryAtPath: [destPath stringByAppendingPathComponent: @"Contents/Resources/Guest"] attributes: nil];
1450   
1451    /* TODO: threaded copy routine as on http://www.cocoadev.com/index.pl?FileCopyProgress */
1452    [fileManager copyPath: pkgSrcPath toPath: [[destPath stringByAppendingPathComponent: @"Contents/Resources/Guest"] stringByAppendingPathComponent: [pkgSrcPath lastPathComponent]] handler: nil];
1453    [progressIndicator setDoubleValue:90];
1454       
1455    /* 2.5 remove absolute pathnames from hda|hdb|hdc|hdd|fda|fdb|cdrom
1456                copy disks outside of .qvm into .app package
1457    */
1458    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:statusText4", @"Localizable", @"cocoaControlController")];
1459   
1460    /* reformat arguments to array containing spaces */
1461    NSMutableArray *arguments = [[NSMutableArray alloc] init];
1462    /* Arguments of thisPC */
1463    NSArray *array = [[pc objectForKey:@"Arguments"] componentsSeparatedByString:@" "];
1464    NSMutableString *option = [[NSMutableString alloc] initWithString:@""];
1465    NSMutableString *argument = [[NSMutableString alloc] init];
1466    int i;
1467    for (i = 1; i < [array count]; i++) {
1468        if ([[array objectAtIndex:i] cString][0] != '-') { // part of an argument
1469            [argument appendFormat:[NSString stringWithFormat:@" %@", [array objectAtIndex:i]]];
1470        } else {
1471            if ([option length] > 0) {
1472                if ([argument isEqual:@""]) {
1473                    [arguments addObject:[NSString stringWithString:option]];
1474                } else {
1475                    [arguments addObject:[NSString stringWithString:option]];
1476                    [arguments addObject:[NSString stringWithString:[argument substringFromIndex:1]]];
1477                }
1478            }
1479            [option setString:[array objectAtIndex:i]];
1480            [argument setString:@""];
1481        }
1482    }
1483    /* last Object */
1484    if ([argument isEqual:@""]) {
1485        [arguments addObject:[NSString stringWithString:option]];
1486    } else {
1487        [arguments addObject:[NSString stringWithString:option]];
1488        [arguments addObject:[NSString stringWithString:[argument substringFromIndex:1]]];
1489    }
1490    /* end reformatting */
1491   
1492    /* which display ? */
1493    if ([[userDefaults objectForKey:@"display"] isEqual:@"OpenGL"]) {
1494    } else if ([[userDefaults objectForKey:@"display"] isEqual:@"Quartz"]) {
1495        [arguments addObject: @"-cocoaquartz"];
1496    } else {
1497        [arguments addObject: @"-cocoaquickdraw"];
1498    }
1499   
1500    /* name of the PC */
1501    [arguments addObject: @"-cocoaname"];
1502    [arguments addObject: [[[pc objectForKey:@"PC Data"] objectForKey:@"name"] stringByAppendingString:@" (Standalone Mode)"]];
1503       
1504    for(i=0; i < [arguments count]; i++) {
1505        if([[arguments objectAtIndex:i] isEqualTo:@"-hda"] || [[arguments objectAtIndex:i] isEqualTo:@"-hdb"] || [[arguments objectAtIndex:i] isEqualTo:@"-hdc"] || [[arguments objectAtIndex:i] isEqualTo:@"-hdd"] || [[arguments objectAtIndex:i] isEqualTo:@"-fda"] || [[arguments objectAtIndex:i] isEqualTo:@"-fdb"] || [[arguments objectAtIndex:i] isEqualTo:@"-cdrom"]) {
1506            if([[arguments objectAtIndex:i+1] isAbsolutePath]) {
1507                // image resides outside of qvm, we have to copy it into app package->qvm and remove the absolute pathname
1508                [progressStatusText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:statusText5", @"Localizable", @"cocoaControlController")];
1509                [fileManager copyPath: [arguments objectAtIndex:i+1] toPath:[destPath stringByAppendingPathComponent: [[NSString stringWithFormat:@"Contents/Resources/Guest/%@", [pkgSrcPath lastPathComponent]] stringByAppendingPathComponent: [[arguments objectAtIndex:i+1] lastPathComponent]]] handler:nil];
1510                [arguments replaceObjectAtIndex:i+1 withObject:[[arguments objectAtIndex:i+1] lastPathComponent]];
1511            }
1512        }
1513    }
1514   
1515    /* 3. save NSMutableArray back to NSString, save arguments in a qemu-binary readable format */
1516    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"exportPCToFlashDrive:progressPanel:statusText6", @"Localizable", @"cocoaControlController")];
1517    NSMutableString * stringArguments = [NSMutableString stringWithCapacity:10];
1518    for(i=0; i< [arguments count]; i++) {
1519        [stringArguments appendFormat:@" %@", [arguments objectAtIndex:i]];
1520    }
1521    // write arguments to file
1522    if(![stringArguments writeToFile:[destPath stringByAppendingPathComponent: @"Contents/Resources/Guest/arguments"] atomically:YES encoding:NSUTF8StringEncoding error:NULL]) {
1523        [self standardAlert: NSLocalizedStringFromTable(@"exportPCToFlashDrive:alert:writeToFile:messageText", @"Localizable", @"cocoaControlController") informativeText:[NSString stringWithFormat: NSLocalizedStringFromTable(@"exportPCToFlashDrive:alert:writeToFile:informativeText", @"Localizable", @"cocoaControlController"), [[pc objectForKey:@"PC Data"] objectForKey:@"name"]]];
1524        // delete exported Guest PC app
1525        [fileManager removeFileAtPath: destPath handler: nil];
1526        /* hide panel */
1527        [NSApp endSheet:progressPanel];
1528        [progressPanel orderOut:self];
1529        return;
1530    }
1531       
1532    /* 4. finish */
1533    [progressIndicator setDoubleValue:100];
1534    [progressIndicator stopAnimation:self];
1535
1536    /* hide panel */
1537    [NSApp endSheet:progressPanel];
1538    [progressPanel orderOut:self];
1539   
1540    /* show finished dialog */
1541    [self standardAlert: NSLocalizedStringFromTable(@"exportPCToFlashDrive:alert:exportFinished:messageText", @"Localizable", @"cocoaControlController") informativeText:[NSString stringWithFormat: NSLocalizedStringFromTable(@"exportPCToFlashDrive:alert:exportFinished:informativeText", @"Localizable", @"cocoaControlController"), [[pc objectForKey:@"PC Data"] objectForKey:@"name"]]];
1542
1543}
1544
1545- (IBAction) exportThisPCToFlashDrive:(id)sender
1546{
1547//  NSLog(@"cocoaControlController: exportThisPCToFlashDrive");
1548
1549    /* no empty line selection */
1550    if ( [table numberOfSelectedRows] == 0 )
1551        return;
1552   
1553    /* don't allow to export a running/saved pc */
1554    id thisPC;
1555    thisPC = [pcs objectAtIndex:[table selectedRow]];
1556   
1557    if (![[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"shutdown"]) {
1558        [self standardAlert: [NSString stringWithFormat: NSLocalizedStringFromTable(@"exportThisPCToFlashDrive:standardAlert", @"Localizable", @"cocoaControlController"),[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]
1559             informativeText: [NSString stringWithFormat: NSLocalizedStringFromTable(@"exportThisPCToFlashDrive:informativeText", @"Localizable", @"cocoaControlController"), [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]];
1560        return;
1561    }
1562   
1563    /* prepare informative alert */
1564    NSAlert *alert = [NSAlert alertWithMessageText: [NSString stringWithFormat: NSLocalizedStringFromTable(@"exportThisPCToFlashDrive:alertWithMessageText", @"Localizable", @"cocoaControlController"), [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]]
1565                      defaultButton: NSLocalizedStringFromTable(@"exportThisPCToFlashDrive:defaultButton", @"Localizable", @"cocoaControlController")
1566                    alternateButton:NSLocalizedStringFromTable(@"exportThisPCToFlashDrive:alternateButton", @"Localizable", @"cocoaControlController")
1567                        otherButton:nil
1568                  informativeTextWithFormat: NSLocalizedStringFromTable(@"exportThisPCToFlashDrive:informativeTextWithFormat", @"Localizable", @"cocoaControlController")];
1569   
1570    // display alert
1571    [alert beginSheetModalForWindow:mainWindow
1572                  modalDelegate:self
1573                 didEndSelector:@selector(exportPCAlertDidEnd:returnCode:contextInfo:)
1574                 contextInfo:thisPC];
1575}
1576
1577- (void) exportPCAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(id)contextInfo
1578{
1579//  NSLog(@"cocoaControlController: exportPCAlertDidEnd");
1580   
1581    [[alert window] orderOut:self];
1582   
1583    if(returnCode == NSOKButton)
1584       [self exportPCToFlashDrive:contextInfo];
1585}
1586
1587- (void) importPCFromFlashDrive:(NSString *)filename
1588{
1589//  NSLog(@"cocoaControlController: importPCFromFlashDrive");
1590   
1591    NSFileManager * fileManager = [NSFileManager defaultManager];
1592    BOOL isDir;
1593    /* check for "Guest" folder inside app package */
1594    if(![fileManager fileExistsAtPath: [filename stringByAppendingPathComponent:@"Contents/Resources/Guest"] isDirectory:&isDir] && isDir) {
1595        [self standardAlert: NSLocalizedStringFromTable(@"importPCFromFlashDrive:alert:notFound:messageText", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importPCFromFlashDrive:alert:notFound:informativeText", @"Localizable", @"cocoaControlController")];
1596        return;
1597    }
1598   
1599    /* search for qvm package */
1600    NSString * file;
1601    NSString * guestDir = [filename stringByAppendingPathComponent:@"Contents/Resources/Guest"];
1602    NSDirectoryEnumerator * dirEnum = [fileManager enumeratorAtPath: guestDir];
1603 
1604    BOOL foundQVM = NO;
1605    while ((file = [dirEnum nextObject])) {
1606        if ([[file pathExtension] isEqualToString: @"qvm"]) {
1607            foundQVM = YES;
1608            break;
1609        }
1610    }
1611   
1612    if(!foundQVM) {
1613        [self standardAlert: NSLocalizedStringFromTable(@"importPCFromFlashDrive:alert:notFound:messageText", @"Localizable", @"cocoaControlController") informativeText: NSLocalizedStringFromTable(@"importPCFromFlashDrive:alert:notFound:informativeText", @"Localizable", @"cocoaControlController")];
1614        return;
1615    }
1616   
1617    file = [filename stringByAppendingPathComponent:[@"Contents/Resources/Guest" stringByAppendingPathComponent:file]];
1618#if kju_debug
1619    NSLog(@"file: %@", file);
1620#endif
1621   
1622    /* ready now, show progressPanel */
1623    [progressTitle setStringValue: NSLocalizedStringFromTable(@"importPCFromFlashDrive:progressPanel:title", @"Localizable", @"cocoaControlController")];
1624    [progressText setStringValue: NSLocalizedStringFromTable(@"importPCFromFlashDrive:progressPanel:text", @"Localizable", @"cocoaControlController")];
1625    [progressStatusText setStringValue: NSLocalizedStringFromTable(@"importPCFromFlashDrive:progressPanel:statusText1", @"Localizable", @"cocoaControlController")];
1626    [progressIndicator setUsesThreadedAnimation:YES];
1627    [progressIndicator setIndeterminate:NO];
1628    [progressIndicator setMaxValue:100];
1629    [progressIndicator setDoubleValue:10];
1630    [progressIndicator startAnimation:self];
1631       
1632    [NSApp beginSheet:progressPanel
1633        modalForWindow:mainWindow
1634        modalDelegate:nil
1635        didEndSelector:nil
1636        contextInfo:nil];
1637       
1638    /* now simply copy over .qvm package */
1639    isDir = NO;
1640    if(!([fileManager fileExistsAtPath:[[userDefaults objectForKey:@"dataPath"] stringByAppendingPathComponent:[file lastPathComponent]] isDirectory:&isDir] && isDir)) {
1641        [fileManager copyPath: file toPath:[[userDefaults objectForKey:@"dataPath"] stringByAppendingPathComponent:[file lastPathComponent]] handler: nil];
1642    } else {
1643        // append "(imported)" to filename and name in configuration.plist
1644        NSMutableDictionary * conf = [NSDictionary dictionaryWithContentsOfFile:[file stringByAppendingPathComponent:@"configuration.plist"]];
1645        NSMutableDictionary * pcdata = [conf objectForKey:@"PC Data"];
1646        [pcdata setObject:[NSString stringWithFormat:@"%@(imported)", [pcdata objectForKey:@"name"]] forKey:@"name"];
1647        [conf setObject:pcdata forKey:@"PC Data"];
1648        NSData *data = [NSPropertyListSerialization
1649        dataFromPropertyList: conf
1650        format: NSPropertyListXMLFormat_v1_0
1651        errorDescription: nil];
1652        [fileManager copyPath: file toPath:[[userDefaults objectForKey:@"dataPath"] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@(imported).qvm", [[file lastPathComponent] substringToIndex:[[file lastPathComponent] length]-4 ]]] handler: nil];
1653        [data writeToFile:[[userDefaults objectForKey:@"dataPath"] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@(imported).qvm/configuration.plist", [[file lastPathComponent] substringToIndex:[[file lastPathComponent] length]-4 ]]] atomically:YES];
1654    }
1655   
1656    /* finish */
1657    [progressIndicator setDoubleValue:100];
1658    [progressIndicator stopAnimation:self];
1659   
1660    /* update Table */
1661    [self loadConfigurations];
1662
1663    /* hide panel */
1664    [NSApp endSheet:progressPanel];
1665    [progressPanel orderOut:self];
1666   
1667    /* show finished dialog */
1668    [self standardAlert: NSLocalizedStringFromTable(@"importPCFromFlashDrive:alert:importFinished:messageText", @"Localizable", @"cocoaControlController") informativeText:[NSString stringWithFormat: NSLocalizedStringFromTable(@"importPCFromFlashDrive:alert:importFinished:informativeText", @"Localizable", @"cocoaControlController"), [[file lastPathComponent] substringToIndex:[[file lastPathComponent] length]-4 ]]];
1669   
1670}
1671
1672- (void) importThisPCFromFlashDriveDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(id)contextInfo
1673{
1674//  NSLog(@"cocoaControlController: importPCFromFlashDriveDidEnd");
1675
1676    /* hide Open Sheet */
1677    [ sheet orderOut:self ];
1678   
1679    if ( returnCode == NSOKButton )
1680        [self importPCFromFlashDrive:[sheet filename]];
1681}
1682
1683- (IBAction)importThisPCFromFlashDrive:(id)sender
1684{
1685//  NSLog(@"cocoaControlController: importThisPCFromFlashDrive");
1686    // open panel
1687    NSOpenPanel *openPanel = [[NSOpenPanel alloc] init];
1688    [openPanel setCanChooseDirectories:NO];
1689    [openPanel setCanCreateDirectories:NO];
1690    [openPanel setCanChooseFiles:YES];
1691    [openPanel beginSheetForDirectory: NSOpenStepRootDirectory()
1692        file:nil
1693        types:[NSArray arrayWithObjects:@"app", nil]
1694        modalForWindow:mainWindow
1695        modalDelegate:self
1696        didEndSelector:@selector(importThisPCFromFlashDriveDidEnd:returnCode:contextInfo:)
1697        contextInfo:sender];
1698
1699}
1700
1701- (BOOL) addArgumentTo:(id)arguments option:(id)option argument:(id)argument filename:filename
1702{
1703//  NSLog(@"addArgumentTo:option:argument:filename:");
1704    NSFileManager *fileManager = [NSFileManager defaultManager];
1705   
1706    /* List of incompatible pre 0.8.0 Arguments */
1707    NSArray *obsoleteArguments = [[NSArray init] arrayWithObjects:@"-user-net", @"-tun-fd", @"-dummy-net", @"-prep", nil];             
1708   
1709    /* sort out old configurations */
1710    if ([obsoleteArguments containsObject:option]) {
1711        [self standardAlert: NSLocalizedStringFromTable(@"addArgumentTo:standardAlert", @"Localizable", @"cocoaControlController") informativeText:[NSString stringWithFormat: NSLocalizedStringFromTable(@"addArgumentTo:informativeText", @"Localizable", @"cocoaControlController"), option]];
1712        return FALSE;
1713    }
1714   
1715    /* do some error checking to avoid QEMU shutting down for incorrect Arguments */
1716   
1717    /* if files have relative Paths, we guess they are stored in .qvm */
1718    if ([option isEqual:@"-hda"] || [option isEqual:@"-hdb"] || [option isEqual:@"-hdc"] || [option isEqual:@"-hdd"] || [option isEqual:@"-cdrom"]) {
1719        [arguments addObject:[NSString stringWithString:option]];
1720        if ([argument isAbsolutePath]) {
1721            [arguments addObject:[NSString stringWithString:argument]]; //remove for bools!!!
1722        } else {
1723            [arguments addObject:[NSString stringWithFormat:@"%@/%@", filename, argument]]; //remove for bools!!!
1724        }
1725
1726    /* "-smb", prepare Folder for Q Filesharing */
1727    } else if ([option isEqual:@"-smb"]) {
1728        /* Q Filesharing */
1729        if ([argument isEqual:@"~/Desktop/Q Shared Files/"]) {
1730            [fileManager createDirectoryAtPath:[@"~/Desktop/Q Shared Files/" stringByExpandingTildeInPath] attributes: nil];
1731            [arguments addObject:@"-smb"];
1732            [arguments addObject:[@"~/Desktop/Q Shared Files/" stringByExpandingTildeInPath]];
1733        /* normal SMB */
1734        } else if ([fileManager fileExistsAtPath:argument]) {
1735            [arguments addObject:@"-smb"];
1736            [arguments addObject:[NSString stringWithString:argument]];
1737        }
1738       
1739    /* standard */       
1740    } else {   
1741        [arguments addObject:[NSString stringWithString:option]];
1742        if (![argument isEqual:@""]) {
1743            [arguments addObject:[NSString stringWithString:argument]];
1744        }
1745    }
1746    return TRUE;
1747}
1748
1749
1750- (void) startThisPC:(id)pc
1751{
1752//  NSLog(@"cocoaControlController: startThisPC");
1753    [self startPC:[NSString stringWithFormat:@"%@/%@.qvm", [userDefaults objectForKey:@"dataPath"], [[pc objectForKey:@"PC Data"] objectForKey:@"name"]]];
1754}
1755
1756
1757- (void) startPC:(NSString *)filename
1758{
1759//  NSLog(@"cocoaControlController: startPC:%@", filename);
1760   
1761    NSData *data = [NSData dataWithContentsOfFile:[NSString stringWithFormat:@"%@/configuration.plist", filename]];
1762    NSMutableDictionary *thisPC;
1763   
1764    if (data) {
1765        thisPC = [[NSPropertyListSerialization
1766            propertyListFromData: data
1767            mutabilityOption: NSPropertyListMutableContainersAndLeaves
1768            format: nil
1769            errorDescription: nil] retain];
1770    } else {
1771        return;
1772    }
1773   
1774    /* upgrade Version 0.1.0.Q */
1775    if ([[thisPC objectForKey:@"Version"] isEqual:@"0.1.0.Q"]) {
1776        NSArray *singleArguments = [[NSArray init] arrayWithObjects:@"-snapshot", @"-nographic", @"-audio-help", @"-localtime", @"-full-screen", @"-win2k-hack", @"-usb", @"-s", @"-S", @"-d", @"-std-vga", nil];
1777        NSEnumerator *enumerator = [[thisPC objectForKey:@"Arguments"] keyEnumerator];
1778        id key;
1779        NSMutableString *newArguments = [[NSMutableString alloc] init];
1780        while ((key = [enumerator nextObject])) {
1781            if ([[thisPC objectForKey:@"Arguments"] objectForKey:key]) {
1782                if ([key isEqual:@"-net"] && [[[thisPC objectForKey:@"Arguments"] objectForKey:key] isEqual:@"user"]) {
1783                    [newArguments appendFormat:[NSString stringWithFormat:@" -net nic"]];
1784                }
1785                if ([singleArguments containsObject:key]) {
1786                    [newArguments appendFormat:[NSString stringWithFormat:@" %@", key]];
1787                } else {
1788                    [newArguments appendFormat:[NSString stringWithFormat:@" %@ %@", key, [[thisPC objectForKey:@"Arguments"] objectForKey:key]]];
1789                }
1790            }
1791        }
1792        [thisPC setObject:newArguments forKey:@"Arguments"];
1793        [thisPC setObject:@"0.2.0.Q" forKey:@"Version"];
1794    }
1795   
1796    /* if this PC is already running, abort */
1797    if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"running"])
1798        return;
1799   
1800    /* save filename in temp */
1801    [[thisPC objectForKey:@"Temporary"] setObject:filename forKey:@"-cocoapath"];
1802   
1803    NSMutableArray *arguments = [[NSMutableArray alloc] init];
1804   
1805    /* Arguments for Q */
1806       
1807    /* which display ? */
1808    if ([[userDefaults objectForKey:@"display"] isEqual:@"OpenGL"]) {
1809    } else if ([[userDefaults objectForKey:@"display"] isEqual:@"Quartz"]) {
1810        [arguments addObject: @"-cocoaquartz"];
1811    } else {
1812        [arguments addObject: @"-cocoaquickdraw"];
1813    }
1814   
1815    /* where to store PC files */
1816    [arguments addObject: @"-cocoapath"];
1817    [arguments addObject: filename];
1818   
1819    /* name of the PC */
1820    [arguments addObject: @"-cocoaname"];
1821    [arguments addObject: [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]];
1822   
1823    /* thumbnails */
1824    [arguments addObject: @"-cocoalivethumbnail"];
1825   
1826    /* WMStopWhenInactive */
1827    if ([[thisPC objectForKey:@"Temporary"] objectForKey:@"WMStopWhenInactive"])
1828        [arguments addObject: @"-wmstopwheninactive"];
1829   
1830    /* Q Windows Drivers */
1831    if ([[thisPC objectForKey:@"Temporary"] objectForKey:@"QWinDrivers"]) {
1832        [arguments addObject: @"-hdb"];
1833        [arguments addObject:[NSString stringWithFormat:@"%@/Contents/Resources/qdrivers.qcow", [[NSBundle mainBundle] bundlePath]]];
1834    }
1835   
1836    /* Arguments of thisPC */
1837    NSArray *array = [[thisPC objectForKey:@"Arguments"] componentsSeparatedByString:@" "];
1838    NSMutableString *option = [[NSMutableString alloc] initWithString:@""];
1839    NSMutableString *argument = [[NSMutableString alloc] init];
1840    int i;
1841    for (i = 1; i < [array count]; i++) {
1842        if ([[array objectAtIndex:i] cString][0] != '-') { //Teil eines Arguments
1843            [argument appendFormat:[NSString stringWithFormat:@" %@", [array objectAtIndex:i]]];
1844        } else {
1845            if ([option length] > 0) {
1846                if ([argument isEqual:@""]) {
1847                    if (![self addArgumentTo:arguments option:option argument:@"" filename:filename])
1848                        return;
1849                } else {
1850                    if (![self addArgumentTo:arguments option:option argument:[argument substringFromIndex:1] filename:filename])
1851                        return;
1852                }
1853            }
1854            [option setString:[array objectAtIndex:i]];
1855            [argument setString:@""];
1856        }
1857    }
1858    if ([argument isEqual:@""]) {
1859        if (![self addArgumentTo:arguments option:option argument:@"" filename:filename])
1860            return;
1861    } else {
1862        if (![self addArgumentTo:arguments option:option argument:[argument substringFromIndex:1] filename:filename])
1863            return;
1864    }
1865               
1866    /* start a saved vm */
1867    if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqual:@"saved"]) {
1868        [arguments addObject: @"-loadvm"];
1869        [arguments addObject: @"kju"];
1870    }
1871
1872#if kju_debug
1873    for (i = 0; i < [arguments count]; i++)
1874        NSLog(@"Argument: %@", [arguments objectAtIndex:i]);
1875#endif
1876
1877    /* save Status */
1878    [[thisPC objectForKey:@"PC Data"] setObject:@"running" forKey:@"state"];
1879    [self savePCConfiguration:thisPC];
1880   
1881    NSTask *task;
1882    task = [[NSTask alloc] init];
1883    [task setLaunchPath: [NSString stringWithFormat:@"%@/Contents/MacOS/%@.app/Contents/MacOS/%@", [[NSBundle mainBundle] bundlePath], [cpuTypes objectForKey:[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"]], [cpuTypes objectForKey:[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"]]]];
1884    [task setArguments: arguments];
1885    [arguments release];
1886   
1887    // check the user defaults
1888    if(![userDefaults boolForKey:@"enableLogToConsole"]) {
1889        // prepare nstask output to grab exit codes and display a standardAlert when the qemu instance crashed
1890        NSPipe * pipe = [[NSPipe alloc] init];
1891        [task setStandardOutput: pipe];
1892        [task setStandardError: pipe];
1893       
1894        [pipe release];
1895    }
1896   
1897    [task launch];
1898   
1899    /* add entry to windowMenu */
1900    NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Q - %@", [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]] action:@selector(qemuWindowMoveToFront:) keyEquivalent:@""];
1901    [menuItem setTarget:self];
1902    [menuItem setTag:[task processIdentifier]];
1903    [windowMenu addItem:menuItem];
1904    [menuItem release];
1905   
1906    /* save PID */
1907    [pcsPIDs setObject:thisPC forKey:[NSString stringWithFormat:@"%d", [task processIdentifier]]];
1908    [pcsTasks setObject:task forKey:[[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]];
1909    [task release];
1910   
1911    /* update Table */
1912    [self loadConfigurations];
1913    [table reloadData];
1914}
1915
1916- (id) tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1917{
1918//  NSLog(@"cocoaControlController: tableView");
1919
1920    id thisPC;
1921    NSString *platform = [NSString stringWithString:@""];
1922   
1923    thisPC = [pcs objectAtIndex:rowIndex];
1924   
1925    if ([[aTableColumn identifier] isEqualTo: @"image"]) {
1926        if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"state"] isEqualTo:@"shutdown"]) {
1927            return [NSImage imageNamed: @"q_table_shutdown.png"];
1928        } else {
1929            return [pcsImages objectAtIndex:rowIndex];
1930        }
1931    }
1932    else if ([[aTableColumn identifier] isEqualTo: @"description"]) {
1933       
1934        if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"x86"]) {
1935            platform = [NSString stringWithString:@"x86 PC"];
1936        } else if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"x86-64"]) {
1937            platform = [NSString stringWithString:@"x86-64 PC"];
1938        } else if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"PowerPC"] && [[[thisPC objectForKey:@"Temporary"] objectForKey:@"-M"] isEqual:@"prep"]) {
1939            platform = [NSString stringWithString:@"PPC PREP"];
1940        } else if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"PowerPC"]) {
1941            platform = [NSString stringWithString:@"PPC PowerMac"];
1942        } else if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"SPARC"]) {
1943            platform = [NSString stringWithString:@"SPARC"];
1944        } else if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"MIPS"]) {
1945            platform = [NSString stringWithString:@"MIPS"];
1946        } else if ([[[thisPC objectForKey:@"PC Data"] objectForKey:@"architecture"] isEqual:@"ARM"]) {
1947            platform = [NSString stringWithString:@"ARM"];
1948        }
1949       
1950        NSMutableAttributedString *attrString = [[[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat: @"%@\n", [[thisPC objectForKey:@"PC Data"] objectForKey:@"name"]] attributes:[NSDictionary dictionaryWithObject: [NSFont boldSystemFontOfSize:[NSFont systemFontSize]] forKey:NSFontAttributeName]] autorelease];
1951//      [attrString appendAttributedString: [[[NSAttributedString alloc] initWithString:[NSString stringWithFormat: NSLocalizedStringFromTable(@"tableView:mb", @"Localizable", @"cocoaControlController"), platform, [[thisPC objectForKey:@"Temporary"] objectForKey:@"-m"]] attributes:[NSDictionary dictionaryWithObject: [NSFont systemFontOfSize:[NSFont smallSystemFontSize]] forKey:NSFontAttributeName]] autorelease]];
1952//      if ([[thisPC objectForKey:@"Temporary"] objectForKey:@"-soundhw"])
1953//          [attrString appendAttributedString: [[[NSAttributedString alloc] initWithString:[NSString stringWithFormat: NSLocalizedStringFromTable(@"tableView:audio", @"Localizable", @"cocoaControlController"),[[thisPC objectForKey:@"Temporary"] objectForKey:@"-soundhw"]] attributes:[NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]] forKey:NSFontAttributeName]] autorelease]];
1954        [attrString appendAttributedString: [[[NSAttributedString alloc] initWithString: NSLocalizedStringFromTable([[thisPC objectForKey:@"PC Data"] objectForKey:@"state"], @"Localizable", @"vmstate") attributes:[NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]] forKey:NSFontAttributeName]] autorelease]];
1955       
1956        return attrString;
1957    }
1958   
1959    return nil;
1960}
1961
1962/* drag'n drop */
1963- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op
1964{
1965    // Add code here to validate the drop
1966    // For now we 'redirect' the drop to an empty row assuming the user wants to create a new Guest PC with a CD-ROM image
1967    NSPasteboard * paste = [info draggingPasteboard];
1968    [table setDropRow:[table numberOfRows] dropOperation: NSTableViewDropAbove];
1969
1970    if([FILE_TYPES containsObject: [[[paste propertyListForType:@"NSFilenamesPboardType"] objectAtIndex:0] pathExtension]]) {
1971        return NSDragOperationEvery;
1972    }
1973    return NSDragOperationNone;
1974}
1975
1976- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
1977            row:(int)row dropOperation:(NSTableViewDropOperation)operation
1978{
1979    NSPasteboard *paste = [info draggingPasteboard];
1980    NSArray *types = [NSArray arrayWithObjects: NSFilenamesPboardType, nil];
1981    NSString *desiredType = [paste availableTypeFromArray:types];
1982    NSData *carriedData = [paste dataForType:desiredType];
1983
1984    if (nil == carriedData)
1985    {
1986        NSRunAlertPanel(@"Paste Error", @"Sorry, but the paste operation failed",
1987            nil, nil, nil);
1988        return NO;
1989    }
1990    else
1991    {
1992        if ([desiredType isEqualToString:NSFilenamesPboardType])
1993        {
1994            /* Live CD handling here: currently we handle the first file to be set as CD-Rom for all Operating Systems of the 'New PC Assistant' showing "Live CD" first */
1995            [self addPCFromDragDrop: [[paste propertyListForType:@"NSFilenamesPboardType"] objectAtIndex:0]];
1996        }
1997        else
1998        {
1999            NSAssert(NO, @"This can't happen");
2000            return NO;
2001        }
2002    }
2003    return YES;
2004}
2005
2006- (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex mouseLocation:(NSPoint)mouseLocation {
2007/*
2008    if ([cell isKindOfClass:[NSTextFieldCell class]]) {
2009        if ([[cell attributedStringValue] size].width > rect->size.width) {
2010            return [cell stringValue];
2011        }
2012    }
2013    return nil;
2014*/
2015    return [[pcs objectAtIndex:rowIndex] objectForKey:@"Arguments"];
2016}
2017
2018- (void) tableDoubleClick:(id)sender
2019{
2020//  NSLog(@"cocoaControlController: tableDoubleClick");
2021
2022    /* no empty line selection */
2023    if ([table selectedRow] < 0)
2024        [self addPC:self];
2025    else if ([[[[pcs objectAtIndex:[table selectedRow]] objectForKey:@"PC Data"] objectForKey:@"state"] isEqualTo:@"running"]) {
2026        /* move QEMU to front */
2027        [qdoserver guestSwitch: @"Q Control" fullscreen:NO nextGuestName:[[[pcs objectAtIndex:[table selectedRow]] objectForKey:@"PC Data"] objectForKey:@"name"]];
2028//      ProcessSerialNumber psn;
2029//      GetProcessForPID( [[pcsTasks objectForKey:[[[pcs objectAtIndex:[table selectedRow]] objectForKey:@"PC Data"] objectForKey:@"name"]] processIdentifier], &psn );
2030//      SetFrontProcess( &psn );
2031    } else {
2032        /* start PC */
2033        [self startThisPC:[pcs objectAtIndex:[table selectedRow]]];
2034    }
2035}
2036
2037- (void) pauseThisPC:(id)pc
2038{
2039//  NSLog(@"cocoaControlController: pauseThisPC");
2040    [qdoserver guestPause:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]];
2041}
2042
2043- (void) playThisPC:(id)pc
2044{
2045//  NSLog(@"cocoaControlController: playThisPC");
2046    [qdoserver guestPause:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]];
2047}
2048
2049- (void) stopThisPC:(id)pc
2050{
2051//  NSLog(@"cocoaControlController: stopThisPC");
2052
2053    /* check if task is running and we can handle it of the qdoserver */
2054    if ([[pcsTasks objectForKey:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]] isRunning] && ([[qdoserver guests] objectForKey:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]] != nil)) { //we try a gentle shudown
2055        [qdoserver guestStop:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]];
2056    } else { //use force
2057        [qdoserver guestUnregisterWithName:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]]; //remove us from the dOServer
2058        [[pcsTasks objectForKey:[[pc objectForKey:@"PC Data"] objectForKey:@"name"]] terminate]; //this sends sigterm, maybe we need something stronger here: "bin kill -9"
2059    }
2060}
2061
2062/* dIWindow */
2063- (IBAction) openDIWindow:(id)sender
2064{
2065//  NSLog(@"cocoaControlController: openDIWindow");
2066
2067    cocoaControlDiskImage *dI = [[cocoaControlDiskImage alloc] init];
2068    if (![NSBundle loadNibNamed:@"cocoaControlDiskImage" owner:dI]) {
2069        NSLog(@"Error loading cocoaControlDiskImage.nib for document!");
2070    } else {
2071        [dI setQSender:nil];
2072    }
2073    [[dI dIWindow] makeKeyAndOrderFront:self];
2074}
2075
2076/* openFreeOSDownloader */
2077- (IBAction) openFreeOSDownloader:(id)sender
2078{
2079//  printf("cocoaControlController: openFreeOSDownloader\n");
2080    if(!downloader) {
2081        downloader = [[cocoaDownloadController alloc] initWithSender:self];
2082        if (![NSBundle loadNibNamed:@"cocoaDownload" owner:downloader]) {
2083            printf("cocoaDownload.nib not loaded!\n");
2084        }
2085    } else {
2086        [downloader initDownloadInterface];
2087    }
2088   
2089    /*
2090    if(!cocoaDownloadController) {
2091        cocoaDownloadController * dl = [[cocoaDownloadController alloc] initWithSender:self];
2092        if (![NSBundle loadNibNamed:@"cocoaDownload" owner:dl]) {
2093            printf("cocoaDownload.nib not loaded!\n");
2094       }
2095    }*/
2096}
2097
2098/* Standard Alert */
2099- (void) standardAlert:(NSString *)messageText informativeText:(NSString *)informativeText
2100{
2101//  NSLog(@"cocoaControlController: standardAlert\n");
2102
2103    NSAlert *alert = [NSAlert alertWithMessageText:messageText
2104        defaultButton:@"OK"
2105        alternateButton:nil
2106        otherButton:nil
2107        informativeTextWithFormat:informativeText];
2108
2109    [alert beginSheetModalForWindow:mainWindow
2110        modalDelegate:self
2111        didEndSelector:nil
2112        contextInfo:nil];
2113}
2114
2115/* check for Update */
2116- (void) getLatestVersion
2117{
2118// NSLog(@"cocoaControlController: getLatestVersion");
2119
2120    unichar dev = 'd';
2121   
2122    if ([[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] characterAtIndex:5] == dev) {
2123        [[NSURL URLWithString:@"http://www.kberg.ch/q/latestVersion.php?d=builds/nightly"] loadResourceDataNotifyingClient:self usingCache:NO];
2124    } else {
2125        [[NSURL URLWithString:@"http://www.kberg.ch/q/latestVersion.php?d=builds"] loadResourceDataNotifyingClient:self usingCache:NO];
2126    }
2127}
2128
2129- (void) URLResourceDidFinishLoading:(NSURL *)sender
2130{
2131// NSLog(@"cocoaControlController: URLResourceDidFinishLoading");
2132
2133    NSData *data = [sender resourceDataUsingCache:YES];
2134
2135    if(data){
2136        NSString *ver = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSISOLatin1StringEncoding];
2137       
2138        if ([ver compare:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]] == NSOrderedDescending) {
2139            NSAlert *alert = [NSAlert alertWithMessageText: NSLocalizedStringFromTable(@"URLResourceDidFinishLoading:alertWithMessageText", @"Localizable", @"cocoaControlController")
2140                defaultButton: NSLocalizedStringFromTable(@"URLResourceDidFinishLoading:defaultButton", @"Localizable", @"cocoaControlController")
2141                alternateButton: NSLocalizedStringFromTable(@"URLResourceDidFinishLoading:alternateButton", @"Localizable", @"cocoaControlController")
2142                otherButton:nil
2143                informativeTextWithFormat:@""];
2144
2145             [alert beginSheetModalForWindow:mainWindow
2146                modalDelegate:self
2147                didEndSelector:@selector(updateAlertDidEnd:returnCode:contextInfo:)
2148                contextInfo:nil];
2149        }
2150    }
2151}
2152
2153- (void) URLResourceDidCancelLoading:(NSURL *)sender
2154{
2155//  NSLog(@"cocoaControlController: URLResourceDidCancelLoading");
2156}
2157
2158- (void)updateAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
2159{
2160//  NSLog(@"cocoaControlController: updateAlertDidEnd Button=%d", returnCode );
2161   
2162    if (returnCode == 1) {
2163        [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.kberg.ch/q"]];
2164    }
2165}
2166@end
Note: See TracBrowser for help on using the browser.