]>
Commit | Line | Data |
---|---|---|
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 | ||
c4bc3f11 | 31 | #include <cmath> |
7c88a3f1 | 32 | #include <dlfcn.h> |
b39b993d | 33 | #include <objc/runtime.h> |
7c88a3f1 | 34 | |
224d48e3 JF |
35 | extern NSString *PSTableCellKey; |
36 | extern "C" UIImage *_UIImageWithName(NSString *); | |
37 | ||
38 | static UIImage *checkImage; | |
39 | static UIImage *uncheckedImage; | |
40 | ||
41 | static BOOL settingsChanged; | |
42 | static NSMutableDictionary *_settings; | |
43 | static NSString *_plist; | |
44 | ||
deed0017 JF |
45 | void AddThemes(NSMutableArray *themesOnDisk, NSString *folder) { |
46 | NSArray *themes([[NSFileManager defaultManager] contentsOfDirectoryAtPath:folder error:NULL]); | |
47 | for (NSString *theme in themes) { | |
48 | if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/%@/Info.plist", folder, theme]]) { | |
49 | if (NSArray *version = [info objectForKey:@"CoreFoundationVersion"]) { | |
50 | size_t count([version count]); | |
51 | if (count == 0 || count > 2) | |
52 | continue; | |
53 | ||
54 | double lower([[version objectAtIndex:0] doubleValue]); | |
55 | if (kCFCoreFoundationVersionNumber < lower) | |
56 | continue; | |
57 | ||
58 | if (count != 1) { | |
59 | double upper([[version objectAtIndex:1] doubleValue]); | |
60 | if (upper <= kCFCoreFoundationVersionNumber) | |
61 | continue; | |
62 | } | |
63 | } | |
64 | } | |
65 | ||
66 | [themesOnDisk addObject:theme]; | |
67 | } | |
68 | } | |
69 | ||
5d644e8b JF |
70 | static void RestartSpringBoard() { |
71 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-icons"); | |
72 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-icons.plist"); | |
73 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-smallicons"); | |
74 | unlink("/User/Library/Caches/com.apple.springboard-imagecache-smallicons.plist"); | |
75 | ||
76 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen"); | |
77 | unlink("/User/Library/Caches/com.apple.SpringBoard.notificationCenterLinen"); | |
78 | ||
79 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.0"); | |
80 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.1"); | |
81 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.2"); | |
82 | unlink("/User/Library/Caches/com.apple.SpringBoard.folderSwitcherLinen.3"); | |
83 | ||
84 | system("rm -rf /User/Library/Caches/SpringBoardIconCache"); | |
85 | system("rm -rf /User/Library/Caches/SpringBoardIconCache-small"); | |
86 | system("rm -rf /User/Library/Caches/com.apple.IconsCache"); | |
87 | system("rm -rf /User/Library/Caches/com.apple.newsstand"); | |
88 | system("rm -rf /User/Library/Caches/com.apple.springboard.sharedimagecache"); | |
89 | system("rm -rf /User/Library/Caches/com.apple.UIStatusBar"); | |
90 | ||
91 | system("rm -rf /User/Library/Caches/BarDialer"); | |
92 | system("rm -rf /User/Library/Caches/BarDialer_selected"); | |
93 | system("rm -rf /User/Library/Caches/BarRecents"); | |
94 | system("rm -rf /User/Library/Caches/BarRecents_selected"); | |
95 | system("rm -rf /User/Library/Caches/BarVM"); | |
96 | system("rm -rf /User/Library/Caches/BarVM_selected"); | |
97 | ||
98 | system("killall -9 lsd"); | |
99 | ||
100 | if (kCFCoreFoundationVersionNumber > 700) // XXX: iOS 6.x | |
101 | system("killall backboardd"); | |
102 | else | |
103 | system("killall SpringBoard"); | |
104 | } | |
105 | ||
265e19b2 JF |
106 | /* [NSObject yieldToSelector:(withObject:)] {{{*/ |
107 | @interface NSObject (wb$yieldToSelector) | |
108 | - (id) wb$yieldToSelector:(SEL)selector withObject:(id)object; | |
109 | - (id) wb$yieldToSelector:(SEL)selector; | |
110 | @end | |
111 | ||
112 | @implementation NSObject (Cydia) | |
113 | ||
114 | - (void) wb$doNothing { | |
115 | } | |
116 | ||
117 | - (void) wb$_yieldToContext:(NSMutableArray *)context { | |
118 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
119 | ||
120 | SEL selector(reinterpret_cast<SEL>([[context objectAtIndex:0] pointerValue])); | |
121 | id object([[context objectAtIndex:1] nonretainedObjectValue]); | |
122 | volatile bool &stopped(*reinterpret_cast<bool *>([[context objectAtIndex:2] pointerValue])); | |
123 | ||
124 | /* XXX: deal with exceptions */ | |
125 | id value([self performSelector:selector withObject:object]); | |
126 | ||
127 | NSMethodSignature *signature([self methodSignatureForSelector:selector]); | |
128 | [context removeAllObjects]; | |
129 | if ([signature methodReturnLength] != 0 && value != nil) | |
130 | [context addObject:value]; | |
131 | ||
132 | stopped = true; | |
133 | ||
134 | [self | |
135 | performSelectorOnMainThread:@selector(wb$doNothing) | |
136 | withObject:nil | |
137 | waitUntilDone:NO | |
138 | ]; | |
139 | ||
140 | [pool release]; | |
141 | } | |
142 | ||
143 | - (id) wb$yieldToSelector:(SEL)selector withObject:(id)object { | |
144 | /*return [self performSelector:selector withObject:object];*/ | |
145 | ||
146 | volatile bool stopped(false); | |
147 | ||
148 | NSMutableArray *context([NSMutableArray arrayWithObjects: | |
149 | [NSValue valueWithPointer:selector], | |
150 | [NSValue valueWithNonretainedObject:object], | |
151 | [NSValue valueWithPointer:const_cast<bool *>(&stopped)], | |
152 | nil]); | |
153 | ||
154 | NSThread *thread([[[NSThread alloc] | |
155 | initWithTarget:self | |
156 | selector:@selector(wb$_yieldToContext:) | |
157 | object:context | |
158 | ] autorelease]); | |
159 | ||
160 | [thread start]; | |
161 | ||
162 | NSRunLoop *loop([NSRunLoop currentRunLoop]); | |
163 | NSDate *future([NSDate distantFuture]); | |
164 | ||
165 | while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); | |
166 | ||
167 | return [context count] == 0 ? nil : [context objectAtIndex:0]; | |
168 | } | |
169 | ||
170 | - (id) wb$yieldToSelector:(SEL)selector { | |
171 | return [self wb$yieldToSelector:selector withObject:nil]; | |
172 | } | |
173 | ||
174 | @end | |
175 | /* }}} */ | |
176 | ||
224d48e3 | 177 | /* Theme Settings Controller {{{ */ |
f4b021a2 | 178 | @interface WBSThemesTableViewCell : UITableViewCell { |
c4bc3f11 JF |
179 | UIImageView *checkmark_; |
180 | UIImageView *icon_; | |
181 | UILabel *name_; | |
f4b021a2 JF |
182 | } |
183 | ||
184 | @end | |
185 | ||
186 | @implementation WBSThemesTableViewCell | |
187 | ||
c4bc3f11 | 188 | - (void) dealloc { |
c4bc3f11 JF |
189 | [checkmark_ release]; |
190 | [icon_ release]; | |
191 | [name_ release]; | |
10ee6126 | 192 | [super dealloc]; |
c4bc3f11 JF |
193 | } |
194 | ||
f4b021a2 JF |
195 | - (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuse { |
196 | if ((self = [super initWithFrame:frame reuseIdentifier:reuse]) != nil) { | |
c4bc3f11 JF |
197 | CGFloat border(48), check(40), icon(64); |
198 | UIView *content([self contentView]); | |
199 | CGSize size([content frame].size); | |
200 | ||
201 | checkmark_ = [[UIImageView alloc] initWithFrame:CGRectMake(std::floor((border - check) / 2), 0, check, size.height)]; | |
202 | [checkmark_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; | |
203 | [content addSubview:checkmark_]; | |
204 | ||
205 | name_ = [[UILabel alloc] initWithFrame:CGRectMake(border, 0, 0, size.height)]; | |
206 | [name_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)]; | |
207 | [content addSubview:name_]; | |
208 | ||
209 | icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(size.width - icon - 48, 0, icon, icon)]; | |
210 | [content addSubview:icon_]; | |
f4b021a2 JF |
211 | } return self; |
212 | } | |
213 | ||
c4bc3f11 JF |
214 | - (void) setCheck:(bool)inactive { |
215 | [self setImage:(inactive ? uncheckedImage : checkImage)]; | |
216 | } | |
217 | ||
f4b021a2 | 218 | - (void) setTheme:(NSDictionary *)theme { |
c4bc3f11 JF |
219 | [name_ setText:[theme objectForKey:@"Name"]]; |
220 | ||
f4b021a2 JF |
221 | NSNumber *active([theme objectForKey:@"Active"]); |
222 | BOOL inactive(active == nil || ![active boolValue]); | |
c4bc3f11 JF |
223 | [self setCheck:inactive]; |
224 | ||
225 | CGRect area([name_ frame]); | |
226 | area.size.width = ([icon_ image] == nil ? self.contentView.frame.size.width : icon_.frame.origin.x) - area.origin.x - 9; | |
227 | [name_ setFrame:area]; | |
f4b021a2 JF |
228 | } |
229 | ||
230 | @end | |
231 | ||
224d48e3 JF |
232 | @interface WBSThemesController: PSViewController <UITableViewDelegate, UITableViewDataSource> { |
233 | UITableView *_tableView; | |
234 | NSMutableArray *_themes; | |
235 | } | |
236 | ||
237 | @property (nonatomic, retain) NSMutableArray *themes; | |
238 | ||
224d48e3 JF |
239 | - (id) initForContentSize:(CGSize)size; |
240 | - (id) view; | |
241 | - (id) navigationTitle; | |
242 | - (void) themesChanged; | |
243 | ||
244 | - (int) numberOfSectionsInTableView:(UITableView *)tableView; | |
245 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section; | |
246 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section; | |
247 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; | |
248 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; | |
249 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath; | |
250 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; | |
251 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath; | |
252 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; | |
7c88a3f1 | 253 | |
224d48e3 JF |
254 | @end |
255 | ||
256 | @implementation WBSThemesController | |
257 | ||
258 | @synthesize themes = _themes; | |
259 | ||
06839c7d | 260 | + (void) initialize { |
224d48e3 JF |
261 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); |
262 | checkImage = [_UIImageWithName(@"UIPreferencesBlueCheck.png") retain]; | |
263 | uncheckedImage = [[UIImage imageWithContentsOfFile:@"/System/Library/PreferenceBundles/WinterBoardSettings.bundle/SearchResultsCheckmarkClear.png"] retain]; | |
264 | [pool release]; | |
265 | } | |
266 | ||
267 | - (id) initForContentSize:(CGSize)size { | |
268 | if ((self = [super initForContentSize:size]) != nil) { | |
269 | self.themes = [_settings objectForKey:@"Themes"]; | |
270 | if (!_themes) { | |
271 | if (NSString *theme = [_settings objectForKey:@"Theme"]) { | |
272 | self.themes = [NSMutableArray arrayWithObject: | |
273 | [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
274 | theme, @"Name", | |
275 | [NSNumber numberWithBool:YES], @"Active", nil]]; | |
276 | [_settings removeObjectForKey:@"Theme"]; | |
277 | } | |
278 | if (!_themes) | |
279 | self.themes = [NSMutableArray array]; | |
280 | [_settings setObject:_themes forKey:@"Themes"]; | |
281 | } | |
282 | ||
283 | NSMutableArray *themesOnDisk([NSMutableArray array]); | |
deed0017 JF |
284 | AddThemes(themesOnDisk, @"/Library/Themes"); |
285 | AddThemes(themesOnDisk, [NSString stringWithFormat:@"%@/Library/SummerBoard/Themes", NSHomeDirectory()]); | |
224d48e3 JF |
286 | |
287 | for (int i = 0, count = [themesOnDisk count]; i < count; i++) { | |
288 | NSString *theme = [themesOnDisk objectAtIndex:i]; | |
289 | if ([theme hasSuffix:@".theme"]) | |
290 | [themesOnDisk replaceObjectAtIndex:i withObject:[theme stringByDeletingPathExtension]]; | |
291 | } | |
292 | ||
293 | NSMutableSet *themesSet([NSMutableSet set]); | |
294 | ||
295 | for (int i = 0, count = [_themes count]; i < count; i++) { | |
296 | NSDictionary *theme([_themes objectAtIndex:i]); | |
297 | NSString *name([theme objectForKey:@"Name"]); | |
298 | ||
299 | if (!name || ![themesOnDisk containsObject:name]) { | |
300 | [_themes removeObjectAtIndex:i]; | |
301 | i--; | |
302 | count--; | |
303 | } else { | |
304 | [themesSet addObject:name]; | |
305 | } | |
306 | } | |
307 | ||
308 | for (NSString *theme in themesOnDisk) { | |
309 | if ([themesSet containsObject:theme]) | |
310 | continue; | |
311 | [themesSet addObject:theme]; | |
312 | ||
313 | [_themes insertObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: | |
314 | theme, @"Name", | |
315 | [NSNumber numberWithBool:NO], @"Active", | |
316 | nil] atIndex:0]; | |
317 | } | |
318 | ||
c4bc3f11 JF |
319 | _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 480-64) style:UITableViewStylePlain]; |
320 | [_tableView setRowHeight:48]; | |
224d48e3 JF |
321 | [_tableView setDataSource:self]; |
322 | [_tableView setDelegate:self]; | |
323 | [_tableView setEditing:YES]; | |
324 | [_tableView setAllowsSelectionDuringEditing:YES]; | |
b39b993d DH |
325 | if ([self respondsToSelector:@selector(setView:)]) |
326 | [self setView:_tableView]; | |
224d48e3 JF |
327 | } |
328 | return self; | |
329 | } | |
330 | ||
331 | - (void) dealloc { | |
332 | [_tableView release]; | |
333 | [_themes release]; | |
334 | [super dealloc]; | |
335 | } | |
336 | ||
337 | - (id) navigationTitle { | |
338 | return @"Themes"; | |
339 | } | |
340 | ||
341 | - (id) view { | |
342 | return _tableView; | |
343 | } | |
344 | ||
345 | - (void) themesChanged { | |
346 | settingsChanged = YES; | |
347 | } | |
348 | ||
349 | /* UITableViewDelegate / UITableViewDataSource Methods {{{ */ | |
350 | - (int) numberOfSectionsInTableView:(UITableView *)tableView { | |
351 | return 1; | |
352 | } | |
353 | ||
354 | - (id) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section { | |
355 | return nil; | |
356 | } | |
357 | ||
358 | - (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section { | |
359 | return _themes.count; | |
360 | } | |
361 | ||
362 | - (id) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | |
f4b021a2 | 363 | WBSThemesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ThemeCell"]; |
224d48e3 | 364 | if (!cell) { |
f4b021a2 | 365 | cell = [[[WBSThemesTableViewCell alloc] initWithFrame:CGRectMake(0, 0, 100, 100) reuseIdentifier:@"ThemeCell"] autorelease]; |
224d48e3 JF |
366 | //[cell setTableViewStyle:UITableViewCellStyleDefault]; |
367 | } | |
368 | ||
369 | NSDictionary *theme([_themes objectAtIndex:indexPath.row]); | |
f4b021a2 | 370 | [cell setTheme:theme]; |
224d48e3 JF |
371 | return cell; |
372 | } | |
373 | ||
374 | - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | |
375 | UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; | |
376 | NSMutableDictionary *theme = [_themes objectAtIndex:indexPath.row]; | |
377 | NSNumber *active = [theme objectForKey:@"Active"]; | |
378 | BOOL inactive = active == nil || ![active boolValue]; | |
379 | [theme setObject:[NSNumber numberWithBool:inactive] forKey:@"Active"]; | |
380 | [cell setImage:(!inactive ? uncheckedImage : checkImage)]; | |
381 | [tableView deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:YES]; | |
382 | [self themesChanged]; | |
383 | } | |
384 | ||
385 | - (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { | |
386 | NSUInteger fromIndex = [fromIndexPath row]; | |
387 | NSUInteger toIndex = [toIndexPath row]; | |
388 | if (fromIndex == toIndex) | |
389 | return; | |
390 | NSMutableDictionary *theme = [[[_themes objectAtIndex:fromIndex] retain] autorelease]; | |
391 | [_themes removeObjectAtIndex:fromIndex]; | |
392 | [_themes insertObject:theme atIndex:toIndex]; | |
393 | [self themesChanged]; | |
394 | } | |
395 | ||
396 | - (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { | |
397 | return UITableViewCellEditingStyleNone; | |
398 | } | |
399 | ||
400 | - (BOOL) tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath { | |
401 | return NO; | |
402 | } | |
403 | ||
404 | - (BOOL) tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { | |
405 | return YES; | |
406 | } | |
407 | /* }}} */ | |
408 | @end | |
409 | /* }}} */ | |
410 | ||
024f98ea | 411 | @interface WBAdvancedController: PSListController { |
224d48e3 JF |
412 | } |
413 | ||
224d48e3 JF |
414 | - (id) specifiers; |
415 | - (void) settingsChanged; | |
224d48e3 JF |
416 | |
417 | @end | |
418 | ||
024f98ea | 419 | @implementation WBAdvancedController |
224d48e3 | 420 | |
024f98ea JF |
421 | - (id) specifiers { |
422 | if (!_specifiers) | |
423 | _specifiers = [[self loadSpecifiersFromPlistName:@"Advanced" target:self] retain]; | |
424 | return _specifiers; | |
7c88a3f1 JF |
425 | } |
426 | ||
024f98ea JF |
427 | - (void) settingsChanged { |
428 | settingsChanged = YES; | |
429 | } | |
7c88a3f1 | 430 | |
024f98ea JF |
431 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec { |
432 | NSString *key([spec propertyForKey:@"key"]); | |
433 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
434 | value = [NSNumber numberWithBool:(![value boolValue])]; | |
435 | [_settings setValue:value forKey:key]; | |
436 | [self settingsChanged]; | |
224d48e3 JF |
437 | } |
438 | ||
024f98ea JF |
439 | - (id) readPreferenceValue:(PSSpecifier *)spec { |
440 | NSString *key([spec propertyForKey:@"key"]); | |
441 | id defaultValue([spec propertyForKey:@"default"]); | |
442 | id plistValue([_settings objectForKey:key]); | |
443 | if (!plistValue) | |
444 | return defaultValue; | |
445 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
446 | plistValue = [NSNumber numberWithBool:(![plistValue boolValue])]; | |
447 | return plistValue; | |
224d48e3 JF |
448 | } |
449 | ||
5d644e8b JF |
450 | - (void) restartSpringBoard { |
451 | RestartSpringBoard(); | |
452 | } | |
453 | ||
43fd3a0b | 454 | - (void) __optimizeThemes { |
265e19b2 JF |
455 | system("/usr/libexec/winterboard/Optimize"); |
456 | } | |
457 | ||
458 | - (void) optimizeThemes { | |
54a4cf27 | 459 | UIAlertView *alert([[[UIAlertView alloc] |
43fd3a0b | 460 | initWithTitle:@"Optimize Themes" |
54a4cf27 | 461 | 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 | 462 | delegate:self |
54a4cf27 JF |
463 | cancelButtonTitle:@"Cancel" |
464 | otherButtonTitles:@"Optimize", nil | |
43fd3a0b JF |
465 | ] autorelease]); |
466 | ||
54a4cf27 JF |
467 | [alert setContext:@"optimize"]; |
468 | [alert setNumberOfRows:1]; | |
469 | [alert show]; | |
43fd3a0b JF |
470 | } |
471 | ||
472 | - (void) _optimizeThemes { | |
265e19b2 JF |
473 | UIView *view([self view]); |
474 | UIWindow *window([view window]); | |
475 | ||
476 | UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window] autorelease]); | |
477 | [hud setText:@"Reticulating Splines\nPlease Wait (Minutes)"]; | |
478 | ||
479 | [window setUserInteractionEnabled:NO]; | |
480 | ||
481 | [window addSubview:hud]; | |
482 | [hud show:YES]; | |
43fd3a0b | 483 | [self wb$yieldToSelector:@selector(__optimizeThemes)]; |
265e19b2 JF |
484 | [hud removeFromSuperview]; |
485 | ||
486 | [window setUserInteractionEnabled:YES]; | |
487 | ||
488 | [self settingsChanged]; | |
489 | } | |
490 | ||
54a4cf27 JF |
491 | - (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button { |
492 | NSString *context([alert context]); | |
43fd3a0b JF |
493 | |
494 | if ([context isEqualToString:@"optimize"]) { | |
54a4cf27 JF |
495 | if (button == [alert firstOtherButtonIndex]) { |
496 | [self performSelector:@selector(_optimizeThemes) withObject:nil afterDelay:0]; | |
43fd3a0b JF |
497 | } |
498 | ||
54a4cf27 JF |
499 | [alert dismissWithClickedButtonIndex:-1 animated:YES]; |
500 | } | |
501 | /*else if ([super respondsToSelector:@selector(alertView:clickedButtonAtIndex:)]) | |
502 | [super alertView:alert clickedButtonAtIndex:button];*/ | |
43fd3a0b JF |
503 | } |
504 | ||
024f98ea JF |
505 | @end |
506 | ||
507 | @interface WBSettingsController: PSListController { | |
508 | } | |
509 | ||
510 | - (id) initForContentSize:(CGSize)size; | |
511 | - (void) dealloc; | |
512 | - (void) suspend; | |
513 | - (void) navigationBarButtonClicked:(int)buttonIndex; | |
514 | - (void) viewWillRedisplay; | |
515 | - (void) pushController:(id)controller; | |
516 | - (id) specifiers; | |
517 | - (void) settingsChanged; | |
518 | - (NSString *) title; | |
519 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec; | |
520 | - (id) readPreferenceValue:(PSSpecifier *)spec; | |
521 | ||
522 | @end | |
523 | ||
524 | @implementation WBSettingsController | |
525 | ||
34b51da8 JF |
526 | - (void) _wb$loadSettings { |
527 | _plist = [[NSString stringWithFormat:@"%@/Library/Preferences/com.saurik.WinterBoard.plist", NSHomeDirectory()] retain]; | |
528 | _settings = [NSMutableDictionary dictionaryWithContentsOfFile:_plist]; | |
529 | ||
530 | bool set; | |
531 | if (_settings != nil) | |
532 | set = true; | |
533 | else { | |
534 | set = false; | |
535 | _settings = [NSMutableDictionary dictionary]; | |
536 | } | |
d236b808 | 537 | |
34b51da8 | 538 | _settings = [_settings retain]; |
d236b808 | 539 | |
34b51da8 JF |
540 | if ([_settings objectForKey:@"SummerBoard"] == nil) |
541 | [_settings setObject:[NSNumber numberWithBool:set] forKey:@"SummerBoard"]; | |
34b51da8 JF |
542 | } |
543 | ||
544 | - (id) initForContentSize:(CGSize)size { | |
545 | if ((self = [super initForContentSize:size]) != nil) { | |
546 | [self _wb$loadSettings]; | |
024f98ea JF |
547 | } return self; |
548 | } | |
549 | ||
550 | - (void) dealloc { | |
551 | [_settings release]; | |
552 | [_plist release]; | |
553 | [super dealloc]; | |
554 | } | |
555 | ||
224d48e3 JF |
556 | - (void) suspend { |
557 | if (!settingsChanged) | |
558 | return; | |
559 | ||
560 | NSData *data([NSPropertyListSerialization dataFromPropertyList:_settings format:NSPropertyListBinaryFormat_v1_0 errorDescription:NULL]); | |
561 | if (!data) | |
562 | return; | |
563 | if (![data writeToFile:_plist options:NSAtomicWrite error:NULL]) | |
564 | return; | |
565 | ||
5d644e8b | 566 | RestartSpringBoard(); |
224d48e3 JF |
567 | } |
568 | ||
b39b993d DH |
569 | - (void) cancelChanges { |
570 | [_settings release]; | |
571 | [_plist release]; | |
b39b993d | 572 | |
34b51da8 JF |
573 | [self _wb$loadSettings]; |
574 | ||
b39b993d DH |
575 | [self reloadSpecifiers]; |
576 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { | |
1d3b613f JF |
577 | [[self navigationItem] setLeftBarButtonItem:nil]; |
578 | [[self navigationItem] setRightBarButtonItem:nil]; | |
838dbf4c DH |
579 | } else { |
580 | [self showLeftButton:nil withStyle:0 rightButton:nil withStyle:0]; | |
b39b993d DH |
581 | } |
582 | settingsChanged = NO; | |
583 | } | |
584 | ||
224d48e3 JF |
585 | - (void) navigationBarButtonClicked:(int)buttonIndex { |
586 | if (!settingsChanged) { | |
587 | [super navigationBarButtonClicked:buttonIndex]; | |
588 | return; | |
589 | } | |
590 | ||
838dbf4c | 591 | if (buttonIndex == 0) { |
b39b993d | 592 | [self cancelChanges]; |
838dbf4c DH |
593 | return; |
594 | } | |
224d48e3 JF |
595 | |
596 | [self suspend]; | |
597 | [self.rootController popController]; | |
598 | } | |
599 | ||
b39b993d DH |
600 | - (void) settingsConfirmButtonClicked:(UIBarButtonItem *)button { |
601 | [self navigationBarButtonClicked:button.tag]; | |
602 | } | |
603 | ||
224d48e3 JF |
604 | - (void) viewWillRedisplay { |
605 | if (settingsChanged) | |
606 | [self settingsChanged]; | |
607 | [super viewWillRedisplay]; | |
608 | } | |
609 | ||
b39b993d DH |
610 | - (void) viewWillAppear:(BOOL)animated { |
611 | if (settingsChanged) | |
612 | [self settingsChanged]; | |
1d3b613f JF |
613 | if ([super respondsToSelector:@selector(viewWillAppear:)]) |
614 | [super viewWillAppear:animated]; | |
b39b993d DH |
615 | } |
616 | ||
224d48e3 JF |
617 | - (void) pushController:(id)controller { |
618 | [self hideNavigationBarButtons]; | |
619 | [super pushController:controller]; | |
620 | } | |
621 | ||
622 | - (id) specifiers { | |
6e9f4e02 JF |
623 | if (!_specifiers) { |
624 | NSMutableArray *specifiers([NSMutableArray array]); | |
625 | for (PSSpecifier *specifier in [self loadSpecifiersFromPlistName:@"WinterBoard" target:self]) { | |
626 | if (NSArray *version = [specifier propertyForKey:@"wb$filter"]) { | |
627 | size_t count([version count]); | |
628 | if (count == 0 || count > 2) | |
629 | continue; | |
630 | ||
631 | double lower([[version objectAtIndex:0] doubleValue]); | |
632 | if (kCFCoreFoundationVersionNumber < lower) | |
633 | continue; | |
634 | ||
635 | if (count != 1) { | |
636 | double upper([[version objectAtIndex:1] doubleValue]); | |
637 | if (upper <= kCFCoreFoundationVersionNumber) | |
638 | continue; | |
639 | } | |
640 | } | |
641 | [specifiers addObject:specifier]; | |
642 | } | |
643 | _specifiers = [specifiers retain]; | |
644 | } | |
224d48e3 JF |
645 | return _specifiers; |
646 | } | |
647 | ||
648 | - (void) settingsChanged { | |
b39b993d DH |
649 | if (![[PSViewController class] instancesRespondToSelector:@selector(showLeftButton:withStyle:rightButton:withStyle:)]) { |
650 | UIBarButtonItem *respringButton([[UIBarButtonItem alloc] initWithTitle:@"Respring" style:UIBarButtonItemStyleDone target:self action:@selector(settingsConfirmButtonClicked:)]); | |
651 | UIBarButtonItem *cancelButton([[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(settingsConfirmButtonClicked:)]); | |
652 | cancelButton.tag = 0; | |
653 | respringButton.tag = 1; | |
1d3b613f JF |
654 | [[self navigationItem] setLeftBarButtonItem:respringButton]; |
655 | [[self navigationItem] setRightBarButtonItem:cancelButton]; | |
b39b993d DH |
656 | [respringButton release]; |
657 | [cancelButton release]; | |
658 | } else { | |
659 | [self showLeftButton:@"Respring" withStyle:2 rightButton:@"Cancel" withStyle:0]; | |
660 | } | |
224d48e3 JF |
661 | settingsChanged = YES; |
662 | } | |
663 | ||
664 | - (NSString *) title { | |
665 | return @"WinterBoard"; | |
666 | } | |
667 | ||
668 | - (void) setPreferenceValue:(id)value specifier:(PSSpecifier *)spec { | |
7c88a3f1 | 669 | NSString *key([spec propertyForKey:@"key"]); |
224d48e3 JF |
670 | if ([[spec propertyForKey:@"negate"] boolValue]) |
671 | value = [NSNumber numberWithBool:(![value boolValue])]; | |
7c88a3f1 | 672 | [_settings setValue:value forKey:key]; |
224d48e3 JF |
673 | [self settingsChanged]; |
674 | } | |
675 | ||
676 | - (id) readPreferenceValue:(PSSpecifier *)spec { | |
677 | NSString *key([spec propertyForKey:@"key"]); | |
678 | id defaultValue([spec propertyForKey:@"default"]); | |
679 | id plistValue([_settings objectForKey:key]); | |
680 | if (!plistValue) | |
681 | return defaultValue; | |
682 | if ([[spec propertyForKey:@"negate"] boolValue]) | |
683 | plistValue = [NSNumber numberWithBool:(![plistValue boolValue])]; | |
684 | return plistValue; | |
685 | } | |
686 | ||
687 | @end | |
b39b993d DH |
688 | |
689 | #define WBSAddMethod(_class, _sel, _imp, _type) \ | |
690 | if (![[_class class] instancesRespondToSelector:@selector(_sel)]) \ | |
691 | class_addMethod([_class class], @selector(_sel), (IMP)_imp, _type) | |
692 | void $PSRootController$popController(PSRootController *self, SEL _cmd) { | |
693 | [self popViewControllerAnimated:YES]; | |
694 | } | |
695 | ||
696 | void $PSViewController$hideNavigationBarButtons(PSRootController *self, SEL _cmd) { | |
697 | } | |
698 | ||
699 | id $PSViewController$initForContentSize$(PSRootController *self, SEL _cmd, CGRect contentSize) { | |
700 | return [self init]; | |
701 | } | |
702 | ||
703 | static __attribute__((constructor)) void __wbsInit() { | |
704 | WBSAddMethod(PSRootController, popController, $PSRootController$popController, "v@:"); | |
705 | WBSAddMethod(PSViewController, hideNavigationBarButtons, $PSViewController$hideNavigationBarButtons, "v@:"); | |
706 | WBSAddMethod(PSViewController, initForContentSize:, $PSViewController$initForContentSize$, "@@:{ff}"); | |
707 | } |