]> git.saurik.com Git - winterboard.git/blame - Settings.mm
CFNotificationCenterAddObserver requires an enum.
[winterboard.git] / Settings.mm
CommitLineData
224d48e3 1/* WinterBoard - Theme Manager for the iPhone
900dc9a5 2 * Copyright (C) 2008-2014 Jay Freeman (saurik)
224d48e3
JF
3*/
4
900dc9a5 5/* GNU Lesser General Public License, Version 3 {{{ */
224d48e3 6/*
900dc9a5
JF
7 * WinterBoard is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
224d48e3 11 *
900dc9a5
JF
12 * WinterBoard is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
224d48e3 16 *
900dc9a5
JF
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with WinterBoard. If not, see <http://www.gnu.org/licenses/>.
19**/
20/* }}} */
224d48e3
JF
21
22#import <Foundation/Foundation.h>
23#import <UIKit/UIKit.h>
b39b993d
DH
24#import <Preferences/PSRootController.h>
25#import <Preferences/PSViewController.h>
224d48e3
JF
26#import <Preferences/PSListController.h>
27#import <Preferences/PSSpecifier.h>
28#import <Preferences/PSTableCell.h>
29#import <UIKit/UINavigationButton.h>
30
7c88a3f1 31#include <dlfcn.h>
b39b993d 32#include <objc/runtime.h>
7c88a3f1
JF
33
34static BOOL (*IsIconHiddenDisplayId)(NSString *);
35static BOOL (*HideIconViaDisplayId)(NSString *);
36static BOOL (*UnHideIconViaDisplayId)(NSString *);
37
fdac1738 38static NSString *WinterBoardDisplayID = @"com.saurik.WinterBoard";
7c88a3f1 39
224d48e3
JF
40extern NSString *PSTableCellKey;
41extern "C" UIImage *_UIImageWithName(NSString *);
42
43static UIImage *checkImage;
44static UIImage *uncheckedImage;
45
46static BOOL settingsChanged;
47static NSMutableDictionary *_settings;
48static NSString *_plist;
49
265e19b2
JF
50/* [NSObject yieldToSelector:(withObject:)] {{{*/
51@interface NSObject (wb$yieldToSelector)
52- (id) wb$yieldToSelector:(SEL)selector withObject:(id)object;
53- (id) wb$yieldToSelector:(SEL)selector;
54@end
55
56@implementation NSObject (Cydia)
57
58- (void) wb$doNothing {
59}
60
61- (void) wb$_yieldToContext:(NSMutableArray *)context {
62 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
63
64 SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue]));
65 id object([[context objectAtIndex:1] nonretainedObjectValue]);
66 volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue]));
67
68 /* XXX: deal with exceptions */
69 id value([self performSelector:selector withObject:object]);
70
71 NSMethodSignature *signature([self methodSignatureForSelector:selector]);
72 [context removeAllObjects];
73 if ([signature methodReturnLength] != 0 && value != nil)
74 [context addObject:value];
75
76 stopped = true;
77
78 [self
79 performSelectorOnMainThread:@selector(wb$doNothing)
80 withObject:nil
81 waitUntilDone:NO
82 ];
83
84 [pool release];
85}
86
87- (id) wb$yieldToSelector:(SEL)selector withObject:(id)object {
88 /*return [self performSelector:selector withObject:object];*/
89
90 volatile bool stopped(false);
91
92 NSMutableArray *context([NSMutableArray arrayWithObjects:
93 [NSValue valueWithPointer:selector],
94 [NSValue valueWithNonretainedObject:object],
95 [NSValue valueWithPointer:const_cast<bool *>(&stopped)],
96 nil]);
97
98 NSThread *thread([[[NSThread alloc]
99 initWithTarget:self
100 selector:@selector(wb$_yieldToContext:)
101 object:context
102 ] autorelease]);
103
104 [thread start];
105
106 NSRunLoop *loop([NSRunLoop currentRunLoop]);
107 NSDate *future([NSDate distantFuture]);
108
109 while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
110
111 return [context count] == 0 ? nil : [context objectAtIndex:0];
112}
113
114- (id) wb$yieldToSelector:(SEL)selector {
115 return [self wb$yieldToSelector:selector withObject:nil];
116}
117
118@end
119/* }}} */
120
224d48e3
JF
121/* Theme Settings Controller {{{ */
122@interface WBSThemesController: PSViewController <UITableViewDelegate, UITableViewDataSource> {
123 UITableView *_tableView;
124 NSMutableArray *_themes;
125}
126
127@property (nonatomic, retain) NSMutableArray *themes;
128
129+ (void) load;
130
131- (id) initForContentSize:(CGSize)size;
132- (id) view;
133- (id) navigationTitle;
134- (void) themesChanged;
135
136- (int) numberOfSectionsInTableView:(UITableView *)tableView;
137- (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section;
138- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section;
139- (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
140- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
141- (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath;
142- (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
143- (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath;
144- (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
7c88a3f1 145
224d48e3
JF
146@end
147
148@implementation WBSThemesController
149
150@synthesize themes = _themes;
151
152+ (void) load {
153 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
154 checkImage = [_UIImageWithName(@"UIPreferencesBlueCheck.png") retain];
155 uncheckedImage = [[UIImage imageWithContentsOfFile:@"/System/Library/PreferenceBundles/WinterBoardSettings.bundle/SearchResultsCheckmarkClear.png"] retain];
156 [pool release];
157}
158
159- (id) initForContentSize:(CGSize)size {
160 if ((self = [super initForContentSize:size]) != nil) {
161 self.themes = [_settings objectForKey:@"Themes"];
162 if (!_themes) {
163 if (NSString *theme = [_settings objectForKey:@"Theme"]) {
164 self.themes = [NSMutableArray arrayWithObject:
165 [NSMutableDictionary dictionaryWithObjectsAndKeys:
166 theme, @"Name",
167 [NSNumber numberWithBool:YES], @"Active", nil]];
168 [_settings removeObjectForKey:@"Theme"];
169 }
170 if (!_themes)
171 self.themes = [NSMutableArray array];
172 [_settings setObject:_themes forKey:@"Themes"];
173 }
174
175 NSMutableArray *themesOnDisk([NSMutableArray array]);
176
177 [themesOnDisk
178 addObjectsFromArray:[[NSFileManager defaultManager]
179 contentsOfDirectoryAtPath:@"/Library/Themes" error:NULL]
180 ];
181
182 [themesOnDisk addObjectsFromArray:[[NSFileManager defaultManager]
183 contentsOfDirectoryAtPath:[NSString stringWithFormat:@"%@/Library/SummerBoard/Themes", NSHomeDirectory()]
184 error:NULL
185 ]];
186
187 for (int i = 0, count = [themesOnDisk count]; i < count; i++) {
188 NSString *theme = [themesOnDisk objectAtIndex:i];
189 if ([theme hasSuffix:@".theme"])
190 [themesOnDisk replaceObjectAtIndex:i withObject:[theme stringByDeletingPathExtension]];
191 }
192
193 NSMutableSet *themesSet([NSMutableSet set]);
194
195 for (int i = 0, count = [_themes count]; i < count; i++) {
196 NSDictionary *theme([_themes objectAtIndex:i]);
197 NSString *name([theme objectForKey:@"Name"]);
198
199 if (!name || ![themesOnDisk containsObject:name]) {
200 [_themes removeObjectAtIndex:i];
201 i--;
202 count--;
203 } else {
204 [themesSet addObject:name];
205 }
206 }
207
208 for (NSString *theme in themesOnDisk) {
209 if ([themesSet containsObject:theme])
210 continue;
211 [themesSet addObject:theme];
212
213 [_themes insertObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:
214 theme, @"Name",
215 [NSNumber numberWithBool:NO], @"Active",
216 nil] atIndex:0];
217 }
218
219 _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480-64) style:UITableViewStyleGrouped];
220 [_tableView setDataSource:self];
221 [_tableView setDelegate:self];
222 [_tableView setEditing:YES];
223 [_tableView setAllowsSelectionDuringEditing:YES];
b39b993d
DH
224 if ([self respondsToSelector:@selector(setView:)])
225 [self setView:_tableView];
224d48e3
JF
226 }
227 return self;
228}
229
230- (void) dealloc {
231 [_tableView release];
232 [_themes release];
233 [super dealloc];
234}
235
236- (id) navigationTitle {
237 return @"Themes";
238}
239
240- (id) view {
241 return _tableView;
242}
243
244- (void) themesChanged {
245 settingsChanged = YES;
246}
247
248/* UITableViewDelegate / UITableViewDataSource Methods {{{ */
249- (int) numberOfSectionsInTableView:(UITableView *)tableView {
250 return 1;
251}
252
253- (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section {
254 return nil;
255}
256
257- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section {
258 return _themes.count;
259}
260
261- (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
262 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ThemeCell"];
263 if (!cell) {
264 cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) reuseIdentifier:@"ThemeCell"] autorelease];
265 //[cell setTableViewStyle:UITableViewCellStyleDefault];
266 }
267
268 NSDictionary *theme([_themes objectAtIndex:indexPath.row]);
269 cell.text = [theme objectForKey:@"Name"];
270 cell.hidesAccessoryWhenEditing = NO;
271 NSNumber *active([theme objectForKey:@"Active"]);
272 BOOL inactive(active == nil || ![active boolValue]);
273 [cell setImage:(inactive ? uncheckedImage : checkImage)];
274 return cell;
275}
276
277- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
278 UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
279 NSMutableDictionary *theme = [_themes objectAtIndex:indexPath.row];
280 NSNumber *active = [theme objectForKey:@"Active"];
281 BOOL inactive = active == nil || ![active boolValue];
282 [theme setObject:[NSNumber numberWithBool:inactive] forKey:@"Active"];
283 [cell setImage:(!inactive ? uncheckedImage : checkImage)];
284 [tableView deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:YES];
285 [self themesChanged];
286}
287
288- (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
289 NSUInteger fromIndex = [fromIndexPath row];
290 NSUInteger toIndex = [toIndexPath row];
291 if (fromIndex == toIndex)
292 return;
293 NSMutableDictionary *theme = [[[_themes objectAtIndex:fromIndex] retain] autorelease];
294 [_themes removeObjectAtIndex:fromIndex];
295 [_themes insertObject:theme atIndex:toIndex];
296 [self themesChanged];
297}
298
299- (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
300 return UITableViewCellEditingStyleNone;
301}
302
303- (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
304 return NO;
305}
306
307- (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
308 return YES;
309}
310/* }}} */
311@end
312/* }}} */
313
024f98ea 314@interface WBAdvancedController: PSListController {
224d48e3
JF
315}
316
224d48e3
JF
317- (id) specifiers;
318- (void) settingsChanged;
224d48e3
JF
319
320@end
321
024f98ea 322@implementation WBAdvancedController
224d48e3 323
024f98ea
JF
324- (id) specifiers {
325 if (!_specifiers)
326 _specifiers = [[self loadSpecifiersFromPlistName:@"Advanced" target:self] retain];
327 return _specifiers;
7c88a3f1
JF
328}
329
024f98ea
JF
330- (void) settingsChanged {
331 settingsChanged = YES;
332}
7c88a3f1 333
024f98ea
JF
334- (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec {
335 NSString *key([spec propertyForKey:@"key"]);
336 if ([[spec propertyForKey:@"negate"] boolValue])
337 value = [NSNumber numberWithBool:(![value boolValue])];
338 [_settings setValue:value forKey:key];
339 [self settingsChanged];
224d48e3
JF
340}
341
024f98ea
JF
342- (id) readPreferenceValue:(PSSpecifier *)spec {
343 NSString *key([spec propertyForKey:@"key"]);
344 id defaultValue([spec propertyForKey:@"default"]);
345 id plistValue([_settings objectForKey:key]);
346 if (!plistValue)
347 return defaultValue;
348 if ([[spec propertyForKey:@"negate"] boolValue])
349 plistValue = [NSNumber numberWithBool:(![plistValue boolValue])];
350 return plistValue;
224d48e3
JF
351}
352
43fd3a0b 353- (void) __optimizeThemes {
265e19b2
JF
354 system("/usr/libexec/winterboard/Optimize");
355}
356
357- (void) optimizeThemes {
54a4cf27 358 UIAlertView *alert([[[UIAlertView alloc]
43fd3a0b 359 initWithTitle:@"Optimize Themes"
54a4cf27 360 message:@"Please note that this setting /replaces/ the PNG files that came with the theme. PNG files that have been iPhone-optimized cannot be viewed on a normal computer unless they are first deoptimized. You can use Cydia to reinstall themes that have been optimized in order to revert to the original PNG files."
43fd3a0b 361 delegate:self
54a4cf27
JF
362 cancelButtonTitle:@"Cancel"
363 otherButtonTitles:@"Optimize", nil
43fd3a0b
JF
364 ] autorelease]);
365
54a4cf27
JF
366 [alert setContext:@"optimize"];
367 [alert setNumberOfRows:1];
368 [alert show];
43fd3a0b
JF
369}
370
371- (void) _optimizeThemes {
265e19b2
JF
372 UIView *view([self view]);
373 UIWindow *window([view window]);
374
375 UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window] autorelease]);
376 [hud setText:@"Reticulating Splines\nPlease Wait (Minutes)"];
377
378 [window setUserInteractionEnabled:NO];
379
380 [window addSubview:hud];
381 [hud show:YES];
43fd3a0b 382 [self wb$yieldToSelector:@selector(__optimizeThemes)];
265e19b2
JF
383 [hud removeFromSuperview];
384
385 [window setUserInteractionEnabled:YES];
386
387 [self settingsChanged];
388}
389
54a4cf27
JF
390- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
391 NSString *context([alert context]);
43fd3a0b
JF
392
393 if ([context isEqualToString:@"optimize"]) {
54a4cf27
JF
394 if (button == [alert firstOtherButtonIndex]) {
395 [self performSelector:@selector(_optimizeThemes) withObject:nil afterDelay:0];
43fd3a0b
JF
396 }
397
54a4cf27
JF
398 [alert dismissWithClickedButtonIndex:-1 animated:YES];
399 }
400 /*else if ([super respondsToSelector:@selector(alertView:clickedButtonAtIndex:)])
401 [super alertView:alert clickedButtonAtIndex:button];*/
43fd3a0b
JF
402}
403
024f98ea
JF
404@end
405
406@interface WBSettingsController: PSListController {
407}
408
409- (id) initForContentSize:(CGSize)size;
410- (void) dealloc;
411- (void) suspend;
412- (void) navigationBarButtonClicked:(int)buttonIndex;
413- (void) viewWillRedisplay;
414- (void) pushController:(id)controller;
415- (id) specifiers;
416- (void) settingsChanged;
417- (NSString *) title;
418- (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec;
419- (id) readPreferenceValue:(PSSpecifier *)spec;
420
421@end
422
423@implementation WBSettingsController
424
425+ (void) load {
426 void *libhide(dlopen("/usr/lib/hide.dylib", RTLD_LAZY));
427 IsIconHiddenDisplayId = reinterpret_cast<BOOL (*)(NSString *)>(dlsym(libhide, "IsIconHiddenDisplayId"));
428 HideIconViaDisplayId = reinterpret_cast<BOOL (*)(NSString *)>(dlsym(libhide, "HideIconViaDisplayId"));
429 UnHideIconViaDisplayId = reinterpret_cast<BOOL (*)(NSString *)>(dlsym(libhide, "UnHideIconViaDisplayId"));
430}
431
34b51da8
JF
432- (void) _wb$loadSettings {
433 _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.WinterBoard.plist", NSHomeDirectory()] retain];
434 _settings = [NSMutableDictionary dictionaryWithContentsOfFile:_plist];
435
436 bool set;
437 if (_settings != nil)
438 set = true;
439 else {
440 set = false;
441 _settings = [NSMutableDictionary dictionary];
442 }
d236b808 443
34b51da8 444 _settings = [_settings retain];
d236b808 445
34b51da8
JF
446 if ([_settings objectForKey:@"SummerBoard"] == nil)
447 [_settings setObject:[NSNumber numberWithBool:set] forKey:@"SummerBoard"];
024f98ea 448
34b51da8
JF
449 [_settings setObject:[NSNumber numberWithBool:IsIconHiddenDisplayId(WinterBoardDisplayID)] forKey:@"IconHidden"];
450}
451
452- (id) initForContentSize:(CGSize)size {
453 if ((self = [super initForContentSize:size]) != nil) {
454 [self _wb$loadSettings];
024f98ea
JF
455 } return self;
456}
457
458- (void) dealloc {
459 [_settings release];
460 [_plist release];
461 [super dealloc];
462}
463
224d48e3
JF
464- (void) suspend {
465 if (!settingsChanged)
466 return;
467
468 NSData *data([NSPropertyListSerialization dataFromPropertyList:_settings format:NSPropertyListBinaryFormat_v1_0 errorDescription:NULL]);
469 if (!data)
470 return;
471 if (![data writeToFile:_plist options:NSAtomicWrite error:NULL])
472 return;
473
7c88a3f1
JF
474 ([[_settings objectForKey:@"IconHidden"] boolValue] ? HideIconViaDisplayId : UnHideIconViaDisplayId)(WinterBoardDisplayID);
475
224d48e3
JF
476 unlink("/User/Library/Caches/com.apple.springboard-imagecache-icons");
477 unlink("/User/Library/Caches/com.apple.springboard-imagecache-icons.plist");
478 unlink("/User/Library/Caches/com.apple.springboard-imagecache-smallicons");
479 unlink("/User/Library/Caches/com.apple.springboard-imagecache-smallicons.plist");
bbea033b 480
546a205b
JF
481 unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen");
482 unlink("/User/Library/Caches/com.apple.SpringBoard.notificationCenterLinen");
bbea033b 483
546a205b
JF
484 unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.0");
485 unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.1");
486 unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.2");
487 unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.3");
7c88a3f1
JF
488
489 system("rm -rf /User/Library/Caches/SpringBoardIconCache");
490 system("rm -rf /User/Library/Caches/SpringBoardIconCache-small");
f64efe8d 491 system("rm -rf /User/Library/Caches/com.apple.IconsCache");
546a205b 492 system("rm -rf /User/Library/Caches/com.apple.newsstand");
bbea033b 493 system("rm -rf /User/Library/Caches/com.apple.springboard.sharedimagecache");
7c88a3f1 494
26bd2bf2 495 system("killall -9 lsd");
c532a1cf
JF
496
497 if (kCFCoreFoundationVersionNumber > 700) // XXX: iOS 6.x
498 system("killall backboardd");
499 else
500 system("killall SpringBoard");
224d48e3
JF
501}
502
b39b993d
DH
503- (void) cancelChanges {
504 [_settings release];
505 [_plist release];
b39b993d 506
34b51da8
JF
507 [self _wb$loadSettings];
508
b39b993d
DH
509 [self reloadSpecifiers];
510 if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) {
1d3b613f
JF
511 [[self navigationItem] setLeftBarButtonItem:nil];
512 [[self navigationItem] setRightBarButtonItem:nil];
838dbf4c
DH
513 } else {
514 [self showLeftButton:nil withStyle:0 rightButton:nil withStyle:0];
b39b993d
DH
515 }
516 settingsChanged = NO;
517}
518
224d48e3
JF
519- (void) navigationBarButtonClicked:(int)buttonIndex {
520 if (!settingsChanged) {
521 [super navigationBarButtonClicked:buttonIndex];
522 return;
523 }
524
838dbf4c 525 if (buttonIndex == 0) {
b39b993d 526 [self cancelChanges];
838dbf4c
DH
527 return;
528 }
224d48e3
JF
529
530 [self suspend];
531 [self.rootController popController];
532}
533
b39b993d
DH
534- (void) settingsConfirmButtonClicked:(UIBarButtonItem *)button {
535 [self navigationBarButtonClicked:button.tag];
536}
537
224d48e3
JF
538- (void) viewWillRedisplay {
539 if (settingsChanged)
540 [self settingsChanged];
541 [super viewWillRedisplay];
542}
543
b39b993d
DH
544- (void) viewWillAppear:(BOOL)animated {
545 if (settingsChanged)
546 [self settingsChanged];
1d3b613f
JF
547 if ([super respondsToSelector:@selector(viewWillAppear:)])
548 [super viewWillAppear:animated];
b39b993d
DH
549}
550
224d48e3
JF
551- (void) pushController:(id)controller {
552 [self hideNavigationBarButtons];
553 [super pushController:controller];
554}
555
556- (id) specifiers {
557 if (!_specifiers)
558 _specifiers = [[self loadSpecifiersFromPlistName:@"WinterBoard" target:self] retain];
559 return _specifiers;
560}
561
562- (void) settingsChanged {
b39b993d
DH
563 if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) {
564 UIBarButtonItem *respringButton([[UIBarButtonItem alloc] initWithTitle:@"Respring" style:UIBarButtonItemStyleDone target:self action:@selector(settingsConfirmButtonClicked:)]);
565 UIBarButtonItem *cancelButton([[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(settingsConfirmButtonClicked:)]);
566 cancelButton.tag = 0;
567 respringButton.tag = 1;
1d3b613f
JF
568 [[self navigationItem] setLeftBarButtonItem:respringButton];
569 [[self navigationItem] setRightBarButtonItem:cancelButton];
b39b993d
DH
570 [respringButton release];
571 [cancelButton release];
572 } else {
573 [self showLeftButton:@"Respring" withStyle:2 rightButton:@"Cancel" withStyle:0];
574 }
224d48e3
JF
575 settingsChanged = YES;
576}
577
578- (NSString *) title {
579 return @"WinterBoard";
580}
581
582- (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec {
7c88a3f1 583 NSString *key([spec propertyForKey:@"key"]);
224d48e3
JF
584 if ([[spec propertyForKey:@"negate"] boolValue])
585 value = [NSNumber numberWithBool:(![value boolValue])];
7c88a3f1 586 [_settings setValue:value forKey:key];
224d48e3
JF
587 [self settingsChanged];
588}
589
590- (id) readPreferenceValue:(PSSpecifier *)spec {
591 NSString *key([spec propertyForKey:@"key"]);
592 id defaultValue([spec propertyForKey:@"default"]);
593 id plistValue([_settings objectForKey:key]);
594 if (!plistValue)
595 return defaultValue;
596 if ([[spec propertyForKey:@"negate"] boolValue])
597 plistValue = [NSNumber numberWithBool:(![plistValue boolValue])];
598 return plistValue;
599}
600
601@end
b39b993d
DH
602
603#define WBSAddMethod(_class, _sel, _imp, _type) \
604 if (![[_class class] instancesRespondToSelector:@selector(_sel)]) \
605 class_addMethod([_class class], @selector(_sel), (IMP)_imp, _type)
606void $PSRootController$popController(PSRootController *self, SEL _cmd) {
607 [self popViewControllerAnimated:YES];
608}
609
610void $PSViewController$hideNavigationBarButtons(PSRootController *self, SEL _cmd) {
611}
612
613id $PSViewController$initForContentSize$(PSRootController *self, SEL _cmd, CGRect contentSize) {
614 return [self init];
615}
616
617static __attribute__((constructor)) void __wbsInit() {
618 WBSAddMethod(PSRootController, popController, $PSRootController$popController, "v@:");
619 WBSAddMethod(PSViewController, hideNavigationBarButtons, $PSViewController$hideNavigationBarButtons, "v@:");
620 WBSAddMethod(PSViewController, initForContentSize:, $PSViewController$initForContentSize$, "@@:{ff}");
621}