Chameleon Applications

Chameleon Applications Svn Source Tree

Root/trunk/ChameleonPrefPane/Sources/ChameleonPrefPane.mm

1//
2// StartupPrefPanePref.m
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}
154}
155//--------------------------------------------------------------------------
156- (void) loadPreferences
157
158{
159id oldGlobalPreferences = [ [NSDictionary dictionaryWithContentsOfFile:
160kPreferencesFilePath ] retain];
161
162mPartitionsDict = [[NSMutableDictionary alloc] init];
163[mPartitionsDict retain];
164
165 if (oldGlobalPreferences!=nil)
166{
167mPreferenceFileVersion= [[oldGlobalPreferences objectForKey: keyPreferencesFileVersion] intValue ];
168[mSwapHD01 setIntValue: [[oldGlobalPreferences objectForKey:keySwapHD01] intValue]];
169[mSwapHD02 setIntValue: [[oldGlobalPreferences objectForKey:keySwapHD02] intValue]];
170[mFreezeParts setIntValue: [[oldGlobalPreferences objectForKey: keyUseFrozenParts] intValue] ];
171[mPartitionsDict addEntriesFromDictionary: [oldGlobalPreferences objectForKey: keyPartitionsList] ];
172}
173else
174{ // Create a preference plist file with Defaults values
175[mSwapHD01 setIntValue: 0];
176[mSwapHD02 setIntValue: 0];
177[mFreezeParts setIntValue: 0];
178// Initialize defaults
179oldGlobalPreferences = [[NSMutableDictionary alloc] init];
180[oldGlobalPreferences setObject: [[NSNumber alloc] initWithInt: CurrentPreferencesFileVersion]
181 forKey: keyPreferencesFileVersion];
182[oldGlobalPreferences setObject: [[NSNumber alloc] initWithBool: false] forKey: keySwapHD01];
183[oldGlobalPreferences setObject: [[NSNumber alloc] initWithBool: false] forKey: keySwapHD02];
184[oldGlobalPreferences setObject:[[NSNumber alloc] initWithBool: false] forKey: keyUseFrozenParts];
185[oldGlobalPreferences setObject: mPartitionsDict forKey: keyPartitionsList];
186
187// Save the preferences file
188[ self savePreferences:oldGlobalPreferences ];
189}
190
191mOptionsDict = [[NSMutableDictionary alloc] init];
192 [mOptionsDict addEntriesFromDictionary:oldGlobalPreferences];
193if (mPartitionsDict!=nil) [mPartitionsDict retain];
194 [oldGlobalPreferences release];
195}
196
197//--------------------------------------------------------------------------
198- (id) init
199{
200self = [super init];
201
202// set the image to display the current file being played
203mMacOSXImage = [self getImageResource: @"MacOSX" ofType: @"png"];
204mWindowsImage = [self getImageResource: @"Windows" ofType: @"png"];
205mLinuxImage = [self getImageResource: @"Linux" ofType: @"png"];
206mCDROMImage = [self getImageResource: @"CDROM" ofType: @"png"];
207mUnknownImage = [self getImageResource: @"Chameleon" ofType: @"tiff"];
208
209
210// Retrieve the com.chameleon.prefPane.plist config
211return self;
212}
213
214//--------------------------------------------------------------------------
215/** When called here, all outlets references are initialized */
216- (void)awakeFromNib
217{ // called more than once, we only need one resource init
218static bool ft=true;
219if(ft)
220{
221ft=false;
222[self loadPreferences];
223[self initBootConfig];
224}
225}
226
227//--------------------------------------------------------------------------
228- (void) initBootConfig
229{
230static bool ft=true;
231
232// Cosmetics setup
233// Setup security for changing boot options
234 AuthorizationItem items = {kAuthorizationRightExecute, 0, NULL, 0};
235 AuthorizationRights rights = {1, &items};
236
237[authView setAuthorizationRights:&rights];
238 authView.delegate = self;
239 [authView updateStatus:nil];
240
241// create the propertylist object that will handle com.apple.Boot.plist
242if(!prop) prop = new PropertyList();
243
244// create the process that will extract the diskutil list infos
245if(!partExtractor) partExtractor = new PartitionExtractor();
246
247if (!prop->isValid())
248{
249std::string sPath;
250AuthorizationRef auth = [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;
251CFStringRef errorString=NULL;
252bool cont =true;
253
254id sForcedPath = [mOptionsDict valueForKey: keyForceBootConfigPath];
255const char * szForcedPath = sForcedPath!=nil ? [sForcedPath UTF8String] : NULL;
256if (szForcedPath && *szForcedPath)
257{
258cont = !prop->open(szForcedPath, &errorString, auth);
259}
260else {
261for(int i=0; szBootPaths[i] && cont; i++)
262{
263sPath = szBootPaths[i];
264sPath += szPropFileName;
265cont = !prop->open(sPath.c_str(), &errorString, auth);
266}
267}
268if (cont)
269{
270if(!ft) return;
271ft=false;
272[mStatusText setTextColor: [NSColor redColor] ];
273if (errorString)
274[mStatusText setStringValue:[NSString stringWithFormat: @"Error while parsing com.apple.Boot.plist : %@",
275 errorString] ];
276else
277[mStatusText setStringValue: @"Error while searching for com.apple.Boot.plist"];
278
279}
280else
281{
282[mStatusText setTextColor: [NSColor grayColor] ];
283NSString* ns = [ [NSString alloc] initWithUTF8String:prop->bootConfigPath() ];
284[mStatusText setStringValue: [NSString stringWithFormat: @"bootConfig: %@", ns] ];
285}
286if (prop->isValid())
287{
288// read options in the plist file
289
290partExtractor->hidePartitions(prop->getStringForKey(kHidePartition));
291partExtractor->renamedPartitions(prop->getStringForKey(kRenamePartition));
292
293// partExtractor->resetSwapping();
294id val = [mOptionsDict valueForKey: keySwapHD01];
295[mSwapHD01 setIntValue: [val intValue] ];
296[self doSwapHD: [val boolValue] save: false src:0 dst:1];
297
298val = [mOptionsDict valueForKey: keySwapHD02];
299[mSwapHD02 setIntValue: [val intValue] ];
300[self doSwapHD: [val boolValue] save: false src:0 dst:2];
301
302[self selectDefaultPartition];
303}
304
305}
306}
307//--------------------------------------------------------------------------
308- (void) selectDefaultPartition
309{
310 if(!authView) return;
311
312[self refreshLockStates];
313
314// try to get the current default partition if any
315if(partExtractor && prop && prop->isValid())
316{
317const char *sdp = prop->getStringForKey(kDefaultPartition);
318sCurrentDefaultPartition = sdp ? sdp : "";
319if (sCurrentDefaultPartition.size())
320{
321int index = partExtractor->getIndexFromHdStringSpec(sCurrentDefaultPartition.c_str());
322if (index>=0)
323{
324[mPartitionsTable selectRowIndexes:
325 [NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
326currentRowSel = index;
327}
328}
329}
330
331}
332
333//--------------------------------------------------------------------------
334- (void) swapDisks: (bool) bSwap src: (int) iSrc dst: (int) iDst;
335{
336if(!partExtractor || !prop || !prop->isValid()) return;
337if (bSwap)
338{
339partExtractor->swapHD(iSrc, iDst);
340}
341
342if ([mFreezeParts intValue]==0)
343partExtractor->extractPartitions();
344else
345[self loadFrozenParts ];
346
347[ self selectDefaultPartition];
348
349}
350
351//--------------------------------------------------------------------------
352- (void) doSwapHD: (int) val save: (bool) doSave src: (int) isrc dst: (int) idst
353{
354
355if( val>0 && ![mFreezeParts intValue]) //on
356{
357[self swapDisks: true src:isrc dst:idst ];
358}
359else
360{
361[self swapDisks: false src:isrc dst:idst ];
362}
363
364if(doSave)
365{
366if (isrc==0 && idst==1)
367[mOptionsDict setObject: [NSNumber numberWithBool: val ? true : false] forKey: keySwapHD01];
368if (isrc==0 && idst==2)
369[mOptionsDict setObject: [NSNumber numberWithBool: val ? true : false] forKey: keySwapHD02];
370[ self savePreferences:mOptionsDict ];
371}
372
373[mPartitionsTable reloadData];
374[mPartitionsTable scrollRowToVisible: 0];
375//[self tableViewSelectionDidChange: nil];
376
377}
378//--------------------------------------------------------------------------
379- (IBAction)onSwapHD: (id)sender
380{
381partExtractor->resetSwapping();
382[self doSwapHD: [mSwapHD01 intValue] save:true src:0 dst:1];
383[self doSwapHD: [mSwapHD02 intValue] save:true src:0 dst:2];
384}
385//--------------------------------------------------------------------------
386- (IBAction)onUseFrozenParts: (id)sender
387{
388bool val = !![sender intValue];
389[mOptionsDict setObject: [NSNumber numberWithBool: val] forKey: keyUseFrozenParts];
390[self savePreferences: mOptionsDict];
391[self onSwapHD: nil];
392}
393
394//--------------------------------------------------------------------------
395- (IBAction)onInjectPartsToFreeze: (id)sender
396{
397int size = partExtractor ? partExtractor->partList().size() : 0;
398if (!size)
399{ // nothing to inject
400NSRunAlertPanel(@"Inject Partitions to Freeze Configuration",
401@"No current partitions to inject, did you check boot config file ?",@"OK", nil,nil);
402return;
403}
404// generate the parts list in preferences proplist
405NSInteger n = NSRunAlertPanel(@"Inject Partitions to Freeze Configuration",
406 @"Are you sure you want to overwrite your Freeze settings with current partition list ?",
407 @"OK", @"Cancel",nil);
408if (n==1)
409{
410// populate the dictionary with the current partitions
411
412// empty dictionary and update key in parent:
413[mOptionsDict removeObjectForKey: keyPartitionsList];
414[mPartitionsDict removeAllObjects ];
415
416// iterate for all entries to add
417char partDesc[256]="";
418char keyPartN[32] = "";
419for (int i=0; i< size; i++)
420{
421
422const PartitionInfo& p =partExtractor->partList()[i];
423
424// format the partition key and descriptor string
425snprintf(keyPartN, sizeof(keyPartN)-1, "partition%02d",i);
426
427snprintf(partDesc, sizeof(partDesc)-1, "%d%c%d%c%s%c%s",
428 p.disk(), cPartDescSep,
429 p.partition(), cPartDescSep,
430 p.label().c_str(), cPartDescSep,
431 p.fsType().c_str());
432
433// write it to the dictionary
434NSString * key = [[NSString alloc] initWithUTF8String: keyPartN];
435NSString * desc = [[NSString alloc] initWithUTF8String: partDesc];
436[mPartitionsDict setObject: desc forKey: key];
437}
438[mOptionsDict setObject: mPartitionsDict forKey: keyPartitionsList];
439[self savePreferences: mOptionsDict];
440}
441}
442//--------------------------------------------------------------------------
443// following DieBuch recommendation : using applescript and system events (thanks!):
444- (IBAction)onRestart: (id)sender
445{
446NSInteger n = NSRunAlertPanel(@"Restarting OS X",
447 @"Are you sure you want to restart your computer now ?",
448 @"OK", @"Cancel", nil);
449if (n==1)
450{
451AuthorizationRef auth = [[authView authorization] authorizationRef];
452executePrivilegedCmd(auth,"/usr/bin/osascript","-e 'tell app \"System Events\" to restart'");
453}
454
455}
456//--------------------------------------------------------------------------
457- (IBAction)onShutdown: (id)sender
458{
459NSInteger n = NSRunAlertPanel(@"Shutting Down OS X",
460 @"Are you sure you want to shut down your computer now ?",
461 @"OK", @"Cancel", /*ThirdButtonHere:*/nil
462 /*, args for a printf-style msg go here */);
463if (n==1)
464{
465system("/usr/bin/osascript -e 'tell app \"System Events\" to shut down'");
466}
467}
468
469- (IBAction)onSleep: (id)sender
470{
471system("/usr/bin/osascript -e 'tell app \"System Events\" to sleep'");
472}
473
474//--------------------------------------------------------------------------
475- (void)tableViewSelectionDidChange:(NSNotification *)notification {
476
477NSTableView* tv= mPartitionsTable;
478if ([tv selectedRow] != currentRowSel)
479{
480currentRowSel = [tv selectedRow];
481if (currentRowSel>=0)
482{
483const std::vector<PartitionInfo>& partInfo = partExtractor->partList();
484char hd[7+1]="hd(n,m)";
485hd[3]= ('0'+partInfo[currentRowSel].disk());
486hd[5]= ('0'+partInfo[currentRowSel].partition());
487AuthorizationRef auth= [self isUnlocked] ? [[authView authorization] authorizationRef] : NULL;
488if(prop->setStringForKey(kDefaultPartition, hd))
489 prop->save(auth);
490}
491}
492
493}
494
495//--------------------------------------------------------------------------
496- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView
497{
498int size = partExtractor ? partExtractor->partList().size() : 0;
499return size;
500}
501
502//--------------------------------------------------------------------------
503- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
504{
505id ret=nil;
506
507const std::vector<PartitionInfo>& partInfo = partExtractor->partList();
508
509if (tableColumn == mPartitionImgColumn)
510{
511switch(partInfo[row].imageIndexFromFs())
512{
513case 0:
514ret = mMacOSXImage;
515break;
516case 1:
517ret = mWindowsImage;
518break;
519case 2:
520ret = mLinuxImage;
521break;
522defualt:
523ret = mUnknownImage;
524break;
525
526}
527}
528else if (tableColumn == mFileSystemColumn)
529{
530ret = [NSString stringWithFormat: @"%s", partInfo[row].fsType().c_str() ];
531}
532else if (tableColumn == mPartitionNameColumn)
533{
534ret = [NSString stringWithFormat: @"%s",
535partInfo[row].label().c_str()
536];
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: 20