Chameleon Applications

Chameleon Applications Svn Source Tree

Root/trunk/ChameleonPrefPane/Sources/ChameleonPrefPane.mm

1//
2// StartupPrefPanePref.mm
3//
4// Created by Rekursor on 1/16/10.
5//
6
7#import "ChameleonPrefPane.h"
8
9#include <process.h>
10#include <property_list.h>
11#include <string>
12
13//--------------------------------------------------------------------------
14// Constants
15//--------------------------------------------------------------------------
16static const char * const szBootPaths[]= {
17"/",
18"/Extra/",
19"/Volumes/EFI/Extra/",
20"/Volumes/Cham/Extra/",
21"/Volumes/BootLoaders/Extra/",
22"/Volumes/RX0/Extra/",
23"/Library/Preferences/SystemConfiguration/",
24NULL
25};
26
27static const char* const szPropFileName = "com.apple.Boot.plist";
28static const char* const kDefaultPartition = "Default Partition";
29static const char* const kHidePartition = "Hide Partition";
30static const char* const kRenamePartition = "Rename Partition";
31
32static const NSString* const kPreferencesFilePath = @"/Library/Preferences/com.chameleon.prefPane.plist";
33static const NSString* const keyPreferencesFileVersion = @"version"; // for future back compatibility
34static const int CurrentPreferencesFileVersion = 0x01; // for future back compatibility
35static const NSString* const keyForceBootConfigPath = @"forceBootConfigPath";
36static const NSString* const keySwapHD01 = @"swapHD01";
37static const NSString* const keySwapHD02 = @"swapHD02";
38static const NSString* const keyUseFrozenParts = @"useFrozenParts";
39static const NSString* const keyPartitionsList = @"partitionsList";
40static const char cPartDescSep = ';'; // partition descriptor separator
41static const char* sPartDescSep = ";"; // cstring version
42//--------------------------------------------------------------------------
43// Static file variables
44//--------------------------------------------------------------------------
45static std::string sCurrentDefaultPartition;
46
47static PartitionExtractor * partExtractor=NULL;
48static PropertyList * prop = NULL;
49static int currentRowSel = -1;
50//--------------------------------------------------------------------------
51
52@implementation ChameleonPrefPane
53
54//--------------------------------------------------------------------------
55/**
56 * SFAuthorization delegates
57 */
58- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view {
59 [self selectDefaultPartition];
60[self refreshLockStates];
61
62}
63
64//--------------------------------------------------------------------------
65- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view {
66 [self refreshLockStates];
67}
68
69//--------------------------------------------------------------------------
70- (void) refreshLockStates
71{
72 [mPartitionsTable setEnabled:[self isUnlocked]];
73 [mSwapHD01 setEnabled:[self isUnlocked]];
74 [mSwapHD02 setEnabled:[self isUnlocked]];
75 [mStatusText setEnabled:[self isUnlocked]];
76 [mFreezeParts setEnabled:[self isUnlocked]];
77 [mInjectFrozenParts setEnabled:[self isUnlocked]];
78}
79
80
81//--------------------------------------------------------------------------
82- (bool)isUnlocked
83{
84 return [authView authorizationState] == SFAuthorizationViewUnlockedState;
85}
86
87//--------------------------------------------------------------------------
88- (id) getImageResource: (NSString *) str ofType: (NSString*) sType
89{
90NSImage * img=nil;
91if(!str) return nil;
92NSBundle * b = [NSBundle bundleForClass:[self class]];
93NSString* sRes = [b pathForResource: str ofType:sType ];
94img = [[NSImage alloc] initWithContentsOfFile: sRes];
95return img;
96}
97
98//--------------------------------------------------------------------------
99-(bool) savePreferences: (NSDictionary*) dict
100{
101std::string sPath = [kPreferencesFilePath UTF8String];
102
103if(dict==nil || sPath.length()==0) return false;
104
105AuthorizationRef auth = [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;
106
107PropertyList::chmodFile(sPath.c_str(), "0777", auth);
108[dict writeToFile:kPreferencesFilePath atomically:YES];
109PropertyList::chmodFile(sPath.c_str(), "0644", auth);
110return true;
111}
112//--------------------------------------------------------------------------
113- (void) loadFrozenParts
114{
115std::vector<PartitionInfo>& partList = partExtractor->editPartList(); //rw
116// iterate for all entries to add
117char keyPartN[32] = "";
118char buffer[256]="";
119int k=0;
120partList.clear();
121for (int i=0; i<[mPartitionsDict count]; i++)
122{
123// get the key
124snprintf(keyPartN, sizeof(keyPartN)-1, "partition%02d",i);
125NSString* obj = [mPartitionsDict objectForKey: [[NSString alloc] initWithUTF8String: keyPartN] ];
126// assign this key if valid
127if (obj!=nil && [obj length]>0)
128{
129PartitionInfo p;
130// parse string
131strncpy(buffer, [obj UTF8String], sizeof(buffer)-1);
132k=0;
133for(const char* word = strtok(buffer,";"); word; word=strtok(NULL,sPartDescSep),k++)
134{
135switch (k) {
136case 0: // parse disk number
137if (isdigit(*word)) p.disk(*word-'0');
138break;
139case 1: // parse partition number
140if (isdigit(*word)) p.partition(*word-'0');
141break;
142case 2: // parse volume label
143p.label(word);
144case 3:
145p.fsType(word);
146break;
147default:
148break;
149}
150}
151partList.push_back(p);
152}
153}
154partExtractor->sortPartList();
155
156}
157//--------------------------------------------------------------------------
158- (void) loadPreferences
159
160{
161id oldGlobalPreferences = [ [NSDictionary dictionaryWithContentsOfFile:
162kPreferencesFilePath ] retain];
163
164mPartitionsDict = [[NSMutableDictionary alloc] init];
165[mPartitionsDict retain];
166
167 if (oldGlobalPreferences!=nil)
168{
169mPreferenceFileVersion= [[oldGlobalPreferences objectForKey: keyPreferencesFileVersion] intValue ];
170[mSwapHD01 setIntValue: [[oldGlobalPreferences objectForKey:keySwapHD01] intValue]];
171[mSwapHD02 setIntValue: [[oldGlobalPreferences objectForKey:keySwapHD02] intValue]];
172[mFreezeParts setIntValue: [[oldGlobalPreferences objectForKey: keyUseFrozenParts] intValue] ];
173[mPartitionsDict addEntriesFromDictionary: [oldGlobalPreferences objectForKey: keyPartitionsList] ];
174}
175else
176{ // Create a preference plist file with Defaults values
177[mSwapHD01 setIntValue: 0];
178[mSwapHD02 setIntValue: 0];
179[mFreezeParts setIntValue: 0];
180// Initialize defaults
181oldGlobalPreferences = [[NSMutableDictionary alloc] init];
182[oldGlobalPreferences setObject: [[NSNumber alloc] initWithInt: CurrentPreferencesFileVersion]
183 forKey: keyPreferencesFileVersion];
184[oldGlobalPreferences setObject: [[NSNumber alloc] initWithBool: false] forKey: keySwapHD01];
185[oldGlobalPreferences setObject: [[NSNumber alloc] initWithBool: false] forKey: keySwapHD02];
186[oldGlobalPreferences setObject:[[NSNumber alloc] initWithBool: false] forKey: keyUseFrozenParts];
187[oldGlobalPreferences setObject: mPartitionsDict forKey: keyPartitionsList];
188
189// Save the preferences file
190[ self savePreferences:oldGlobalPreferences ];
191}
192
193mOptionsDict = [[NSMutableDictionary alloc] init];
194 [mOptionsDict addEntriesFromDictionary:oldGlobalPreferences];
195if (mPartitionsDict!=nil) [mPartitionsDict retain];
196 [oldGlobalPreferences release];
197}
198
199//--------------------------------------------------------------------------
200- (id) init
201{
202self = [super init];
203
204// set the image to display the current file being played
205mMacOSXImage = [self getImageResource: @"MacOSX" ofType: @"png"];
206mWindowsImage = [self getImageResource: @"Windows" ofType: @"png"];
207mLinuxImage = [self getImageResource: @"Linux" ofType: @"png"];
208mCDROMImage = [self getImageResource: @"CDROM" ofType: @"png"];
209mUnknownImage = [self getImageResource: @"Chameleon" ofType: @"tiff"];
210
211
212// Retrieve the com.chameleon.prefPane.plist config
213return self;
214}
215
216//--------------------------------------------------------------------------
217/** When called here, all outlets references are initialized */
218- (void)awakeFromNib
219{ // called more than once, we only need one resource init
220static bool ft=true;
221if(ft)
222{
223ft=false;
224[self loadPreferences];
225[self initBootConfig];
226}
227}
228
229//--------------------------------------------------------------------------
230- (void) initBootConfig
231{
232static bool ft=true;
233
234// Cosmetics setup
235// Setup security for changing boot options
236 AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};
237 AuthorizationRights rights = {1, &items};
238
239[authView setAuthorizationRights:&rights];
240 authView.delegate = self;
241 [authView updateStatus:nil];
242
243// create the propertylist object that will handle com.apple.Boot.plist
244if(!prop) prop = new PropertyList();
245
246// create the process that will extract the diskutil list infos
247if(!partExtractor) partExtractor = new PartitionExtractor();
248
249if (!prop->isValid())
250{
251std::string sPath;
252AuthorizationRef auth = [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;
253CFStringRef errorString=NULL;
254bool cont =true;
255
256id sForcedPath = [mOptionsDict valueForKey: keyForceBootConfigPath];
257const char * szForcedPath = sForcedPath!=nil ? [sForcedPath UTF8String] : NULL;
258if (szForcedPath && *szForcedPath)
259{
260cont = !prop->open(szForcedPath, &errorString, auth);
261}
262else {
263for(int i=0; szBootPaths[i] && cont; i++)
264{
265sPath = szBootPaths[i];
266sPath += szPropFileName;
267cont = !prop->open(sPath.c_str(), &errorString, auth);
268}
269}
270if (cont)
271{
272if(!ft) return;
273ft=false;
274[mStatusText setTextColor: [NSColor redColor] ];
275if (errorString)
276[mStatusText setStringValue:[NSString stringWithFormat: @"Error while parsing com.apple.Boot.plist : %@",
277 errorString] ];
278else
279[mStatusText setStringValue: @"Error while searching for com.apple.Boot.plist"];
280
281}
282else
283{
284[mStatusText setTextColor: [NSColor grayColor] ];
285NSString* ns = [ [NSString alloc] initWithUTF8String:prop->bootConfigPath() ];
286[mStatusText setStringValue: [NSString stringWithFormat: @"bootConfig: %@", ns] ];
287}
288if (prop->isValid())
289{
290// read options in the plist file
291
292partExtractor->hidePartitions(prop->getStringForKey(kHidePartition));
293partExtractor->renamedPartitions(prop->getStringForKey(kRenamePartition));
294
295// partExtractor->resetSwapping();
296id val = [mOptionsDict valueForKey: keySwapHD01];
297[mSwapHD01 setIntValue: [val intValue] ];
298[self doSwapHD: [val boolValue] save: false src:0 dst:1];
299
300val = [mOptionsDict valueForKey: keySwapHD02];
301[mSwapHD02 setIntValue: [val intValue] ];
302[self doSwapHD: [val boolValue] save: false src:0 dst:2];
303
304[self selectDefaultPartition];
305}
306
307}
308}
309//--------------------------------------------------------------------------
310- (void) selectDefaultPartition
311{
312 if(!authView) return;
313
314[self refreshLockStates];
315
316// try to get the current default partition if any
317if(partExtractor && prop && prop->isValid())
318{
319const char *sdp = prop->getStringForKey(kDefaultPartition);
320sCurrentDefaultPartition = sdp ? sdp : "";
321if (sCurrentDefaultPartition.size())
322{
323int index = partExtractor->getIndexFromHdStringSpec(sCurrentDefaultPartition.c_str());
324if (index>=0)
325{
326[mPartitionsTable selectRowIndexes:
327 [NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
328currentRowSel = index;
329}
330}
331}
332
333}
334
335//--------------------------------------------------------------------------
336- (void) swapDisks: (bool) bSwap src: (int) iSrc dst: (int) iDst;
337{
338if(!partExtractor || !prop || !prop->isValid()) return;
339if (bSwap)
340{
341partExtractor->swapHD(iSrc, iDst);
342}
343
344if ([mFreezeParts intValue]==0)
345partExtractor->extractPartitions();
346else
347[self loadFrozenParts ];
348
349[ self selectDefaultPartition];
350
351}
352
353//--------------------------------------------------------------------------
354- (void) doSwapHD: (int) val save: (bool) doSave src: (int) isrc dst: (int) idst
355{
356
357if( val>0 && ![mFreezeParts intValue]) //on
358{
359[self swapDisks: true src:isrc dst:idst ];
360}
361else
362{
363[self swapDisks: false src:isrc dst:idst ];
364}
365
366if(doSave)
367{
368if (isrc==0 && idst==1)
369[mOptionsDict setObject: [NSNumber numberWithBool: val ? true : false] forKey: keySwapHD01];
370if (isrc==0 && idst==2)
371[mOptionsDict setObject: [NSNumber numberWithBool: val ? true : false] forKey: keySwapHD02];
372[ self savePreferences:mOptionsDict ];
373}
374
375[mPartitionsTable reloadData];
376[mPartitionsTable scrollRowToVisible: 0];
377//[self tableViewSelectionDidChange: nil];
378
379}
380//--------------------------------------------------------------------------
381- (IBAction)onSwapHD: (id)sender
382{
383partExtractor->resetSwapping();
384[self doSwapHD: [mSwapHD01 intValue] save:true src:0 dst:1];
385[self doSwapHD: [mSwapHD02 intValue] save:true src:0 dst:2];
386}
387//--------------------------------------------------------------------------
388- (IBAction)onUseFrozenParts: (id)sender
389{
390bool val = !![sender intValue];
391[mOptionsDict setObject: [NSNumber numberWithBool: val] forKey: keyUseFrozenParts];
392[self savePreferences: mOptionsDict];
393[self onSwapHD: nil];
394}
395
396//--------------------------------------------------------------------------
397- (IBAction)onInjectPartsToFreeze: (id)sender
398{
399int size = partExtractor ? partExtractor->partList().size() : 0;
400if (!size)
401{ // nothing to inject
402NSRunAlertPanel(@"Inject Partitions to Freeze Configuration",
403@"No current partitions to inject, did you check boot config file ?",@"OK", nil,nil);
404return;
405}
406// generate the parts list in preferences proplist
407NSInteger n = NSRunAlertPanel(@"Inject Partitions to Freeze Configuration",
408 @"Are you sure you want to overwrite your Freeze settings with current partition list ?",
409 @"OK", @"Cancel",nil);
410if (n==1)
411{
412// populate the dictionary with the current partitions
413
414// empty dictionary and update key in parent:
415[mOptionsDict removeObjectForKey: keyPartitionsList];
416[mPartitionsDict removeAllObjects ];
417
418// iterate for all entries to add
419char partDesc[256]="";
420char keyPartN[32] = "";
421for (int i=0; i< size; i++)
422{
423
424const PartitionInfo& p =partExtractor->partList()[i];
425
426// format the partition key and descriptor string
427snprintf(keyPartN, sizeof(keyPartN)-1, "partition%02d",i);
428
429snprintf(partDesc, sizeof(partDesc)-1, "%d%c%d%c%s%c%s",
430 p.disk(), cPartDescSep,
431 p.partition(), cPartDescSep,
432 p.clabel(), cPartDescSep,
433 p.cfsType());
434
435// write it to the dictionary
436NSString * key = [[NSString alloc] initWithUTF8String: keyPartN];
437NSString * desc = [[NSString alloc] initWithUTF8String: partDesc];
438[mPartitionsDict setObject: desc forKey: key];
439}
440[mOptionsDict setObject: mPartitionsDict forKey: keyPartitionsList];
441[self savePreferences: mOptionsDict];
442}
443}
444//--------------------------------------------------------------------------
445// following DieBuch recommendation : using applescript and system events (thanks!):
446- (IBAction)onRestart: (id)sender
447{
448NSInteger n = NSRunAlertPanel(@"Restarting OS X",
449 @"Are you sure you want to restart your computer now ?",
450 @"OK", @"Cancel", nil);
451if (n==1)
452{
453AuthorizationRef auth = [[authView authorization] authorizationRef];
454executePrivilegedCmd(auth,"/usr/bin/osascript","-e 'tell app \"System Events\" to restart'");
455}
456
457}
458//--------------------------------------------------------------------------
459- (IBAction)onShutdown: (id)sender
460{
461NSInteger n = NSRunAlertPanel(@"Shutting Down OS X",
462 @"Are you sure you want to shut down your computer now ?",
463 @"OK", @"Cancel", /*ThirdButtonHere:*/nil
464 /*, args for a printf-style msg go here */);
465if (n==1)
466{
467system("/usr/bin/osascript -e 'tell app \"System Events\" to shut down'");
468}
469}
470
471- (IBAction)onSleep: (id)sender
472{
473system("/usr/bin/osascript -e 'tell app \"System Events\" to sleep'");
474}
475
476//--------------------------------------------------------------------------
477- (void)tableViewSelectionDidChange:(NSNotification *)notification {
478
479NSTableView* tv= mPartitionsTable;
480if ([tv selectedRow] != currentRowSel)
481{
482currentRowSel = [tv selectedRow];
483if (currentRowSel>=0)
484{
485const std::vector<PartitionInfo>& partInfo = partExtractor->partList();
486char hd[7+1]="hd(n,m)";
487hd[3]= ('0'+partInfo[currentRowSel].disk());
488hd[5]= ('0'+partInfo[currentRowSel].partition());
489AuthorizationRef auth= [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;
490if(prop->setStringForKey(kDefaultPartition, hd))
491 prop->save(auth);
492}
493}
494
495}
496
497//--------------------------------------------------------------------------
498- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
499{
500int size = partExtractor ? partExtractor->partList().size() : 0;
501return size;
502}
503
504//--------------------------------------------------------------------------
505- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
506{
507id ret=nil;
508
509const std::vector<PartitionInfo>& partInfo = partExtractor->partList();
510
511if (tableColumn == mPartitionImgColumn)
512{
513switch(partInfo[row].imageIndexFromFs())
514{
515case 0:
516ret = mMacOSXImage;
517break;
518case 1:
519ret = mWindowsImage;
520break;
521case 2:
522ret = mLinuxImage;
523break;
524defualt:
525ret = mUnknownImage;
526break;
527
528}
529}
530else if (tableColumn == mFileSystemColumn)
531{
532ret = [NSString stringWithFormat: @"%s", partInfo[row].cfsType() ];
533}
534else if (tableColumn == mPartitionNameColumn)
535{
536ret = [NSString stringWithFormat: @"%s", partInfo[row].clabel()];
537}
538else if (tableColumn == mPartitionIDColumn)
539{
540ret= [NSString stringWithFormat: @"hd(%d,%d)",
541partInfo[row].disk(),
542partInfo[row].partition()
543];
544}
545
546return ret;
547}
548//--------------------------------------------------------------------------
549
550@end
551

Archive Download this file

Revision: 37