1 | //␊ |
2 | // PreferencesControllerBase.mm␊ |
3 | // ChameleonPrefPane␊ |
4 | //␊ |
5 | // Created by Rekursor on 1/22/10.␊ |
6 | //␊ |
7 | ␊ |
8 | #import "PreferencesControllerBase.h"␊ |
9 | #import "KernOptionsParser.h"␊ |
10 | #include <string>␊ |
11 | #include <list>␊ |
12 | #include <map>␊ |
13 | #include <ctype.h>␊ |
14 | ␊ |
15 | ␊ |
16 | //--------------------------------------------------------------------------␊ |
17 | static std::list< id<GroupControllerProtocol> > groupList;␊ |
18 | ␊ |
19 | // for unix-like options types␊ |
20 | static std::map<void*, std::string> IdToUCmdList;␊ |
21 | static KernOptionsParser kernelFlags;␊ |
22 | ␊ |
23 | //--------------------------------------------------------------------------␊ |
24 | ␊ |
25 | ␊ |
26 | @implementation PreferencesControllerBase␊ |
27 | ␊ |
28 | -(ChameleonPrefPane*) chameleon { return [ChameleonPrefPane instance]; }␊ |
29 | ␊ |
30 | ␊ |
31 | //--------------------------------------------------------------------------␊ |
32 | - (id) init␊ |
33 | {␊ |
34 | ␉self = [super init];␊ |
35 | ␉␊ |
36 | ␉[PreferencesControllerBase registerPreferencesGroup: self];␊ |
37 | ␉␊ |
38 | ␉return self;␊ |
39 | }␉␊ |
40 | //--------------------------------------------------------------------------␊ |
41 | - (id) getResourcePath: (NSString *) str ofType: (NSString*) sType␊ |
42 | {␊ |
43 | ␉if(!str) return nil;␊ |
44 | ␉NSBundle * b = [NSBundle bundleForClass:[self class]];␊ |
45 | ␉id sRes = [b pathForResource: str ofType:sType ];␊ |
46 | ␉return sRes;␊ |
47 | }␊ |
48 | ␊ |
49 | ␊ |
50 | //--------------------------------------------------------------------------␊ |
51 | // get authorisation from main panel lock␊ |
52 | - (AuthorizationRef) getAuthorization␊ |
53 | {␊ |
54 | ␉AuthorizationRef auth= [[self chameleon] isUnlocked] ? ␊ |
55 | ␉[[ [self chameleon]->authView authorization] authorizationRef] : NULL;␊ |
56 | ␉return auth;␊ |
57 | }␊ |
58 | //--------------------------------------------------------------------------␊ |
59 | + (void) registerPreferencesGroup:(id) myGroup␊ |
60 | {␊ |
61 | ␉groupList.push_back(myGroup);␊ |
62 | }␊ |
63 | ␊ |
64 | - (void) synchronizeTextContent:(const BootOptionDesc*) bod withValue:(int) val␊ |
65 | {␊ |
66 | ␉if(!bod || !bod->contentID) return;␊ |
67 | ␉id item = (id) bod->contentID;␊ |
68 | if ( [item isKindOfClass: [NSTextField class]] ||␊ |
69 | ␉␉[item isKindOfClass: [NSComboBox class]])␊ |
70 | ␉{␊ |
71 | ␉␉[item setEnabled: val];␊ |
72 | ␉␉[item setEditable: val];␊ |
73 | ␉}␊ |
74 | ␉␊ |
75 | }␊ |
76 | //--------------------------------------------------------------------------␊ |
77 | + (void) refreshLockState: (id) item␊ |
78 | {␊ |
79 | ␉const BootOptionDesc * bod = BootProp::instance().findOption(item);␊ |
80 | ␉if(!bod) bod=BootProp::instance().findOptionContent(item);␊ |
81 | ␉if(bod) [PreferencesControllerBase␉refreshBodLockState: bod];␊ |
82 | ␉else␉[item setEnabled: [[ChameleonPrefPane instance] isUnlocked]];␊ |
83 | }␊ |
84 | //--------------------------------------------------------------------------␊ |
85 | + (void) refreshBodLockState: (const BootOptionDesc*) bod␊ |
86 | {␊ |
87 | ␉if (!bod) return;␊ |
88 | ␉bool isUnlocked = [[ChameleonPrefPane instance] isUnlocked];␊ |
89 | ␉if (bod->ID) [(id) bod->ID setEnabled: isUnlocked];␊ |
90 | ␉int val = bod->ID ? [(id) bod->ID intValue] : 0;␊ |
91 | if ( [(id) bod->contentID isKindOfClass: [NSTextField class]] ||␊ |
92 | ␉␉[(id) bod->contentID isKindOfClass: [NSComboBox class]])␊ |
93 | ␉{␊ |
94 | ␉␉[(id) bod->contentID setEnabled: (isUnlocked && val) ];␊ |
95 | ␉␉[(id) bod->contentID setEditable: (isUnlocked && val) ];␊ |
96 | ␉}␊ |
97 | ␉else␊ |
98 | ␉␉[(id) bod->contentID setEnabled: isUnlocked];␊ |
99 | }␊ |
100 | ␊ |
101 | //--------------------------------------------------------------------------␊ |
102 | + (void) refreshLockStates␊ |
103 | {␊ |
104 | ␉for (const BootOptionDesc* bod=BootProp::instance().firstOption(); ␊ |
105 | ␉␉ bod; ␊ |
106 | ␉␉ bod=BootProp::instance().nextOption())␊ |
107 | ␉{␊ |
108 | ␉␉[self refreshLockState: (id) bod->ID ];␊ |
109 | ␉␉if (bod->contentID) [self refreshLockState: (id) bod->contentID ];␊ |
110 | ␉}␊ |
111 | ␉␊ |
112 | }␊ |
113 | //--------------------------------------------------------------------------␊ |
114 | + (void) doForEachGroup: (GroupAction) action withOption:(id) option␊ |
115 | {␊ |
116 | ␉std::list<id>::iterator it;␊ |
117 | ␉for (it=groupList.begin(); it!=groupList.end(); it++)␊ |
118 | ␉{␊ |
119 | ␉␉switch (action) {␊ |
120 | ␉␉␉case SetDefaultValues:␊ |
121 | ␉␉␉␉[*it setDefaultsValues: option];␊ |
122 | ␉␉␉␉break;␊ |
123 | ␉␉␉case RefreshLockStates:␊ |
124 | ␉␉␉␉[*it refreshLockStates ];␊ |
125 | ␉␉␉␉[PreferencesControllerBase refreshLockStates];␊ |
126 | ␉␉␉␉break;␊ |
127 | ␉␉␉case LoadPreferencesOptions:␊ |
128 | ␉␉␉␉[*it loadOptionsFromPreferencesFile: option];␊ |
129 | ␉␉␉␉break;␊ |
130 | ␉␉␉case LoadBootConfigOptions:␊ |
131 | ␉␉␉␉[*it loadOptionsFromBootFile];␊ |
132 | ␉␉␉␉break;␊ |
133 | ␉␉␉case AddOptionsDesc:␊ |
134 | ␉␉␉␉[*it addOptionsDesc];␊ |
135 | ␉␉␉␉break;␊ |
136 | ␉␉␉case SaveBootConfigOptions:␊ |
137 | ␉␉␉␉break;␊ |
138 | ␉␉␉default:␊ |
139 | ␉␉␉␉break;␊ |
140 | ␉␉}␊ |
141 | ␉}␊ |
142 | }␊ |
143 | ␉␉ ␊ |
144 | //--------------------------------------------------------------------------␊ |
145 | + (void) loadOptionsFromBootFile␊ |
146 | {␊ |
147 | ␉// parse unix like command string:␊ |
148 | ␉kernelFlags.parseOptions(BootProp::instance().getStringForKey(kKernelFlags));␊ |
149 | ␉␊ |
150 | ␉for (const BootOptionDesc* bod=BootProp::instance().firstOption(); ␊ |
151 | ␉␉ bod; ␊ |
152 | ␉␉ bod=BootProp::instance().nextOption())␊ |
153 | ␉{␊ |
154 | ␉␉[PreferencesControllerBase loadOptionFromBootFile:(id)bod->ID ];␊ |
155 | ␉}␊ |
156 | }␊ |
157 | ␊ |
158 | //--------------------------------------------------------------------------␊ |
159 | ␊ |
160 | + (void) loadOptionFromBootFile:(id) optionID␊ |
161 | {␊ |
162 | ␉const BootOptionDesc* bod = BootProp::instance().findOption(optionID);␊ |
163 | ␉if (!bod) ␊ |
164 | ␉{␊ |
165 | ␉␉NSRunAlertPanel(@"Error Parsing Option",@"loadOptionFromBootFile failed",@"OK", nil, nil);␊ |
166 | ␉␉return;␊ |
167 | ␉} ␊ |
168 | ␉␊ |
169 | ␉const char * stringForKey = BootProp::instance().getStringForKey(bod->Name);␊ |
170 | ␉std::string s = stringForKey ? trim(stringForKey) : "";␊ |
171 | ␉std::string def = trim(bod->Default ? bod->Default : "");␊ |
172 | ␉␊ |
173 | ␉switch (bod->Type) ␊ |
174 | ␉{␊ |
175 | ␉␉case OptionYesNo:␊ |
176 | ␉␉␉if (s.length()>0) ␊ |
177 | ␉␉␉␉[(NSButton*)optionID setIntValue: (toupper(s[0])=='Y' ? 1 : 0 ) ];␊ |
178 | ␉␉␉else␊ |
179 | ␉␉␉␉[(NSButton*)optionID setIntValue: (toupper(def[0])=='Y' ? 1 : 0 ) ];␊ |
180 | ␉␉␉break;␊ |
181 | ␉␉␉␊ |
182 | ␉␉case OptionKernel1:␊ |
183 | ␉␉{␊ |
184 | ␉␉␉int val = (s.length()>0 ? 1 : 0 );␊ |
185 | ␉␉␉[(NSButton*)optionID setIntValue: val ];␊ |
186 | ␉␉␉[(NSTextField*) bod->contentID setStringValue: ␊ |
187 | ␉␉␉ [[NSString alloc] initWithUTF8String: s.c_str()] ];␊ |
188 | ␉␉␉[(NSTextField*) bod->contentID setEnabled: val ? true : false]; ␊ |
189 | ␉␉␉[(NSTextField*) bod->contentID setEditable: val ? true : false]; ␊ |
190 | ␉␉}␊ |
191 | ␉␉␉break;␊ |
192 | ␉␉case OptionFileString:␊ |
193 | ␉␉case OptionString:␊ |
194 | ␉␉{␊ |
195 | ␉␉␉int val = (s.length()>0 ? 1 : 0 );␊ |
196 | ␉␉␉[(NSButton*)optionID setIntValue: val ];␊ |
197 | ␉␉␉[(NSTextField*) bod->contentID setStringValue: ␊ |
198 | ␉␉␉␉[[NSString alloc] initWithUTF8String: s.c_str()] ];␊ |
199 | ␉␉␉[(NSTextField*) bod->contentID setEnabled: val ? true: false]; ␊ |
200 | ␉␉␉[(NSTextField*) bod->contentID setEditable: val ? true : false]; ␊ |
201 | ␉␉}␊ |
202 | ␉␉␉break;␊ |
203 | ␉␉␉␊ |
204 | ␉␉case OptionUnix:␊ |
205 | ␉␉case OptionKernel:␊ |
206 | ␉␉{␊ |
207 | ␉␉␉std::string s = kernelFlags.stringFromKey(bod->Name);␊ |
208 | ␉␉␉if (s.length()>0)␊ |
209 | ␉␉␉{␊ |
210 | ␉␉␉␉[(NSButton*)optionID setIntValue: 1 ];␊ |
211 | ␉␉␉␉if(bod->Type==OptionKernel)␊ |
212 | ␉␉␉␉{␊ |
213 | ␉␉␉␉␉[(NSTextField*) bod->contentID setStringValue: ␊ |
214 | ␉␉␉␉␉ [[NSString alloc] initWithUTF8String: ␊ |
215 | ␉␉␉␉␉␉kernelFlags.rightMember(s).c_str()] ];␊ |
216 | ␉␉␉␉␉[(NSTextField*) bod->contentID setEnabled: true]; ␊ |
217 | ␉␉␉␉␉[(NSTextField*) bod->contentID setEditable: true]; ␊ |
218 | ␉␉␉␉}␊ |
219 | ␊ |
220 | ␉␉␉}␊ |
221 | ␉␉␉else␊ |
222 | ␉␉␉{ // set the default for thiso option ␊ |
223 | ␉␉␉␉[(NSButton*)optionID setIntValue: (bod->Default[0] ? 1 :0) ];␊ |
224 | ␉␉␉␉if(bod->Type==OptionKernel)␊ |
225 | ␉␉␉␉{␊ |
226 | ␉␉␉␉␉[(NSTextField*) bod->contentID setEnabled: false]; ␊ |
227 | ␉␉␉␉␉[(NSTextField*) bod->contentID setEditable: false]; ␊ |
228 | ␉␉␉␉}␊ |
229 | ␊ |
230 | ␉␉␉}␊ |
231 | ␉␉}␊ |
232 | ␉␉␉break;␊ |
233 | ␉␉default:␊ |
234 | ␉␉␉break;␊ |
235 | ␉}␊ |
236 | ␉␊ |
237 | }␊ |
238 | ␊ |
239 | //--------------------------------------------------------------------------␊ |
240 | - (void) loadPreferences␊ |
241 | {␊ |
242 | ␉[ [ChameleonPrefPane instance] loadPreferences];␊ |
243 | }␊ |
244 | ␊ |
245 | //--------------------------------------------------------------------------␊ |
246 | - (bool) savePreferences␊ |
247 | {␊ |
248 | ␉return [ [ChameleonPrefPane instance] savePreferences: [self preferencesFile] ];␊ |
249 | ␉␊ |
250 | }␊ |
251 | //--------------------------------------------------------------------------␊ |
252 | // update the boot Config with one option change and its associated desc ␊ |
253 | - (bool) saveBootConfig: (id) sender withBootOptionDesc: (BootOptionDesc*) bod␊ |
254 | {␊ |
255 | ␉if(!bod) ␊ |
256 | ␉{␊ |
257 | ␉␉return false;␊ |
258 | ␉}␊ |
259 | ␉// load boot config file so that we don't risk to loose␊ |
260 | ␉// externally modified parameters␊ |
261 | ␉␊ |
262 | ␉int val = [(NSButton*) sender intValue ];␊ |
263 | ␉std::string sDefaultValue = trim(bod->Default ? bod->Default : "");␊ |
264 | ␉bool status = false;␊ |
265 | ␉std::string name = trim(bod->Name);␊ |
266 | ␉␊ |
267 | ␉switch (bod->Type) {␊ |
268 | ␉␉case OptionYesNo:␊ |
269 | ␉␉{␊ |
270 | ␉␉␉std::string sVal = val ? "Yes" : "No"; ␊ |
271 | ␉␉␉if (sDefaultValue.length()==0) sDefaultValue␉= "No";␊ |
272 | ␉␉␉// Avoid populating bootConfig with unnecessary options:␊ |
273 | ␉␉␉if (sVal == sDefaultValue) ␊ |
274 | ␉␉␉␉status = BootProp::instance().removeKeyAndValue(name.c_str());␊ |
275 | ␉␉␉else␊ |
276 | ␉␉␉␉status = BootProp::instance().setStringForKey(name, sVal.c_str());␊ |
277 | ␉␉}␊ |
278 | ␉␉␉break;␊ |
279 | ␉␉case OptionUnix:␊ |
280 | ␉␉␉if (!val)␉kernelFlags.removeFlag(name);␊ |
281 | ␉␉␉else␉␉kernelFlags.addFlag(name);␊ |
282 | ␉␉␉BootProp::instance().setStringForKey(kKernelFlags,kernelFlags.options());␊ |
283 | ␉␉␉status = true;␊ |
284 | ␉␉␉break;␊ |
285 | ␉␉case OptionKernel:␊ |
286 | ␉␉{␊ |
287 | ␉␉␉std::string contentValue = trim(␊ |
288 | ␉␉␉[ [(NSTextField*) bod->contentID stringValue] UTF8String ]);␊ |
289 | ␉␉␉kernelFlags.removeFlag(kernelFlags.stringFromKey(bod->Name));␊ |
290 | ␉␉␉if(val && contentValue.length()>0)␊ |
291 | ␉␉␉{␊ |
292 | ␉␉␉␉std::string concat = trim(name);␊ |
293 | ␉␉␉␉concat+= "=";␊ |
294 | ␉␉␉␉concat+= trim(contentValue);␊ |
295 | ␉␉␉␉␊ |
296 | ␉␉␉␉kernelFlags.addFlag(concat);␊ |
297 | ␉␉␉}␊ |
298 | ␉␉␉BootProp::instance().setStringForKey(kKernelFlags,kernelFlags.options());␊ |
299 | ␉␉␉status = true;␊ |
300 | ␉␉}␊ |
301 | ␉␉␉break;␊ |
302 | ␉␉case OptionKernel1:␊ |
303 | ␉␉case OptionFileString:␊ |
304 | ␉␉case OptionString:␊ |
305 | ␉␉␉// Avoid populating bootConfig with unnecessary options:␊ |
306 | ␉␉␉if (val == 0 && bod->Type!=OptionKernel1) ␊ |
307 | ␉␉␉␉status = BootProp::instance().removeKeyAndValue(bod->Name);␊ |
308 | ␉␉␉else␊ |
309 | ␉␉␉{␊ |
310 | ␉␉␉␉std::string contentValue =␊ |
311 | ␉␉␉␉␉[ [(NSTextField*) bod->contentID stringValue] UTF8String ];␊ |
312 | ␉␉␉␉if (contentValue.length()>0)␊ |
313 | ␉␉␉␉␉status = BootProp::instance().setStringForKey(bod->Name, contentValue.c_str());␊ |
314 | ␉␉␉␉else {␊ |
315 | ␉␉␉␉␉return false; // no content to save so don't save it␊ |
316 | ␉␉␉␉}␊ |
317 | ␊ |
318 | ␉␉␉}␊ |
319 | ␉␉␉break;␊ |
320 | ␉␉default:␊ |
321 | ␉␉␉break;␊ |
322 | ␉}␊ |
323 | ␉␊ |
324 | ␉// Now save the bootConfig␊ |
325 | ␉AuthorizationRef auth = [self getAuthorization ];␊ |
326 | ␉if (status)␉status = BootProp::instance().save(auth);␊ |
327 | ␊ |
328 | ␉return status;␊ |
329 | }␊ |
330 | //--------------------------------------------------------------------------␊ |
331 | -(NSMutableDictionary*) preferencesFile␊ |
332 | {␉␊ |
333 | ␉return [[ChameleonPrefPane instance] preferencesFile];␊ |
334 | }␊ |
335 | ␊ |
336 | //--------------------------------------------------------------------------␊ |
337 | -(NSMutableDictionary*) preferencesParts ␊ |
338 | { ␊ |
339 | ␉return [[ChameleonPrefPane instance] preferencesParts];␊ |
340 | }␊ |
341 | ␊ |
342 | //--------------------------------------------------------------------------␊ |
343 | - (bool) handleSender: (id) sender␊ |
344 | {␊ |
345 | ␊ |
346 | ␉const BootOptionDesc * bod = BootProp::instance().findOption(sender);␊ |
347 | ␉␊ |
348 | ␉if (!bod) {␊ |
349 | ␉␉bod = BootProp::instance().findOptionContent(sender);␊ |
350 | ␉␉NSTextField* textField = (NSTextField*) sender;␊ |
351 | ␉␉std::string content = [[textField stringValue] UTF8String ];␊ |
352 | ␉␉if(bod->ID!=nil) sender = (id) bod->ID;␊ |
353 | ␉}␊ |
354 | ␉else␊ |
355 | ␉{␊ |
356 | ␉␉␊ |
357 | ␉␉int state = [sender intValue];␊ |
358 | ␉␊ |
359 | ␉␉switch (bod->Type) {␊ |
360 | ␉␉␉case OptionKernel:␊ |
361 | ␉␉␉case OptionKernel1:␊ |
362 | ␉␉␉case OptionFileString:␊ |
363 | ␉␉␉case OptionString:␊ |
364 | ␉␉␉[(NSTextField*) bod->contentID setEnabled: state ? true : false];␊ |
365 | ␉␉␉[(NSTextField*) bod->contentID setEditable: state ? true : false];␊ |
366 | ␉␉␉if (state && bod->Type==OptionFileString)␊ |
367 | ␉␉␉{␊ |
368 | ␉␉␉␉NSString* f = [self selectAnyFile ];␊ |
369 | ␉␉␉␉if (f) ␊ |
370 | ␉␉␉␉{␊ |
371 | ␉␉␉␉␉[(NSTextField*)bod->contentID setStringValue: f];␊ |
372 | ␉␉␉␉␉[f release];␉␊ |
373 | ␉␉␉␉}␊ |
374 | ␉␉␉}␊ |
375 | ␉␉␉[self synchronizeTextContent: bod withValue: state];␊ |
376 | ␉␉␉␉break;␊ |
377 | ␉␉␉default:␊ |
378 | ␉␉␉␉break;␊ |
379 | ␉␉}␊ |
380 | ␉}␉␊ |
381 | ␉if(![self saveBootConfig: sender withBootOptionDesc: (BootOptionDesc*) bod] && !bod->contentID )␊ |
382 | ␉{ // Couldn't save, so warn user ...␊ |
383 | ␉␉NSRunAlertPanel(@"Error saving bootConfig", @"Could not save com.apple.Boot.plist",␊ |
384 | ␉␉␉␉␉␉␉@"OK", nil, nil);␊ |
385 | ␉}␊ |
386 | ␉return true;␊ |
387 | }␊ |
388 | ␊ |
389 | - (bool) executeTaskAndWaitForTermination: (NSString*)taskPath␊ |
390 | {␊ |
391 | ␉NSTask *task = [[NSTask alloc] init];␊ |
392 | ␉[task setLaunchPath:taskPath];␊ |
393 | ␉[task launch];␊ |
394 | ␉␊ |
395 | ␉[task waitUntilExit]; // wait for process termination␊ |
396 | ␉int status = [task terminationStatus];␊ |
397 | ␉␊ |
398 | ␉return (status == 0) ? true : false;␊ |
399 | }␊ |
400 | ␊ |
401 | - (NSString*) selectPlistFile:(NSString*) name␊ |
402 | {␊ |
403 | ␉NSArray* fileTypes = [NSArray arrayWithObjects: @"plist", nil];␊ |
404 | ␉NSString * s= [self selectFileWithFileTypes: @"/" withName: name ␊ |
405 | ␉␉␉␉␉␉␉ withTypes: fileTypes];␊ |
406 | ␉[fileTypes release];␊ |
407 | ␉return s;␊ |
408 | }␊ |
409 | ␊ |
410 | - (NSString*) selectAnyFile␊ |
411 | {␊ |
412 | ␉return [self selectFileWithFileTypes: @"/" withName: nil withTypes:nil];␊ |
413 | }␊ |
414 | ␊ |
415 | - (NSString*) selectFileWithFileTypes:(NSString*) dir withName: (NSString*) name withTypes:(NSArray*) fileTypes␊ |
416 | {␊ |
417 | int result;␊ |
418 | NSOpenPanel *oPanel = [NSOpenPanel openPanel];␊ |
419 | [oPanel setAllowsMultipleSelection:NO];␊ |
420 | ␉[oPanel setCanChooseFiles:YES];␊ |
421 | ␉[oPanel setCanChooseDirectories:NO];␊ |
422 | ␉[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"AppleShowAllFiles"];␊ |
423 | ␊ |
424 | result = [oPanel runModalForDirectory: dir file: name types:fileTypes];␊ |
425 | if (result == NSOKButton) ␊ |
426 | ␉{␊ |
427 | NSArray *filesToOpen = [oPanel filenames];␊ |
428 | int count = [filesToOpen count];␊ |
429 | if(count>0)␊ |
430 | ␉␉{␊ |
431 | NSString *aFile = [filesToOpen objectAtIndex:0];␊ |
432 | ␉␉␉return aFile;␊ |
433 | ␉␉␉␊ |
434 | ␉␉}␊ |
435 | }␊ |
436 | ␊ |
437 | ␉return nil;␊ |
438 | }␊ |
439 | @end␊ |
440 | |