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

Archive Download this file

Revision: 43