]>
Commit | Line | Data |
---|---|---|
d5168fd6 JF |
1 | /* WinterBoard - Theme Manager for the iPhone |
2 | * Copyright (C) 2008 Jay Freeman (saurik) | |
3 | */ | |
4 | ||
5 | /* | |
6 | * Redistribution and use in source and binary | |
7 | * forms, with or without modification, are permitted | |
8 | * provided that the following conditions are met: | |
9 | * | |
10 | * 1. Redistributions of source code must retain the | |
11 | * above copyright notice, this list of conditions | |
12 | * and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the | |
14 | * above copyright notice, this list of conditions | |
15 | * and the following disclaimer in the documentation | |
16 | * and/or other materials provided with the | |
17 | * distribution. | |
18 | * 3. The name of the author may not be used to endorse | |
19 | * or promote products derived from this software | |
20 | * without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, | |
24 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE | |
27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
28 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
29 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
32 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
34 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
35 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
36 | */ | |
37 | ||
889cb4f2 | 38 | #define _trace() NSLog(@"WB:_trace(%u)", __LINE__); |
ca13798d | 39 | #define _transient |
62b2dbad | 40 | |
d5168fd6 JF |
41 | #include <objc/runtime.h> |
42 | #include <objc/message.h> | |
43 | ||
2435118f JF |
44 | extern "C" { |
45 | #include <mach-o/nlist.h> | |
46 | } | |
47 | ||
d5168fd6 | 48 | #import <Foundation/Foundation.h> |
2435118f | 49 | #import <CoreGraphics/CoreGraphics.h> |
d5168fd6 JF |
50 | |
51 | #import <UIKit/UIColor.h> | |
889cb4f2 | 52 | #import <UIKit/UIFont.h> |
d5168fd6 JF |
53 | #import <UIKit/UIImage.h> |
54 | #import <UIKit/UIImageView.h> | |
26c43b47 | 55 | #import <UIKit/UINavigationBarBackground.h> |
394d1eb5 | 56 | #import <UIKit/UIWebDocumentView.h> |
62b2dbad | 57 | |
889cb4f2 JF |
58 | #import <UIKit/NSString-UIStringDrawing.h> |
59 | #import <UIKit/NSString-UIStringDrawingDeprecated.h> | |
60 | ||
2435118f JF |
61 | #import <UIKit/UIImage-UIImageDeprecated.h> |
62 | ||
62b2dbad | 63 | #import <UIKit/UIView-Geometry.h> |
d5168fd6 | 64 | #import <UIKit/UIView-Hierarchy.h> |
62b2dbad | 65 | #import <UIKit/UIView-Rendering.h> |
d5168fd6 JF |
66 | |
67 | #import <SpringBoard/SBApplication.h> | |
394d1eb5 | 68 | #import <SpringBoard/SBApplicationIcon.h> |
d5168fd6 | 69 | #import <SpringBoard/SBAppWindow.h> |
62b2dbad | 70 | #import <SpringBoard/SBButtonBar.h> |
d5168fd6 | 71 | #import <SpringBoard/SBContentLayer.h> |
889cb4f2 | 72 | #import <SpringBoard/SBIconLabel.h> |
08454e3a | 73 | #import <SpringBoard/SBStatusBarContentsView.h> |
d5fb6e01 | 74 | #import <SpringBoard/SBStatusBarController.h> |
08454e3a | 75 | #import <SpringBoard/SBStatusBarTimeView.h> |
d5168fd6 JF |
76 | #import <SpringBoard/SBUIController.h> |
77 | ||
d5fb6e01 JF |
78 | #import <MediaPlayer/MPVideoView.h> |
79 | #import <MediaPlayer/MPVideoView-PlaybackControl.h> | |
80 | ||
d5168fd6 JF |
81 | #import <CoreGraphics/CGGeometry.h> |
82 | ||
26c43b47 JF |
83 | @interface NSDictionary (WinterBoard) |
84 | - (UIColor *) colorForKey:(NSString *)key; | |
889cb4f2 | 85 | - (BOOL) boolForKey:(NSString *)key; |
26c43b47 JF |
86 | @end |
87 | ||
88 | @implementation NSDictionary (WinterBoard) | |
89 | ||
90 | - (UIColor *) colorForKey:(NSString *)key { | |
91 | NSString *value = [self objectForKey:key]; | |
92 | if (value == nil) | |
93 | return nil; | |
94 | /* XXX: incorrect */ | |
95 | return nil; | |
96 | } | |
97 | ||
889cb4f2 JF |
98 | - (BOOL) boolForKey:(NSString *)key { |
99 | if (NSString *value = [self objectForKey:key]) | |
100 | return [value boolValue]; | |
101 | return NO; | |
102 | } | |
103 | ||
26c43b47 JF |
104 | @end |
105 | ||
d5fb6e01 | 106 | bool Debug_ = true; |
ca13798d | 107 | bool Engineer_ = false; |
889cb4f2 | 108 | |
d5168fd6 JF |
109 | /* WinterBoard Backend {{{ */ |
110 | #define WBPrefix "wb_" | |
111 | ||
112 | void WBInject(const char *classname, const char *oldname, IMP newimp, const char *type) { | |
113 | Class _class = objc_getClass(classname); | |
114 | if (_class == nil) | |
115 | return; | |
116 | if (!class_addMethod(_class, sel_registerName(oldname), newimp, type)) | |
2435118f | 117 | NSLog(@"WB:Error: failed to inject [%s %s]", classname, oldname); |
d5168fd6 JF |
118 | } |
119 | ||
889cb4f2 | 120 | void WBRename(bool instance, const char *classname, const char *oldname, IMP newimp) { |
d5168fd6 | 121 | Class _class = objc_getClass(classname); |
26c43b47 | 122 | if (_class == nil) { |
889cb4f2 JF |
123 | if (Debug_) |
124 | NSLog(@"WB:Warning: cannot find class [%s]", classname); | |
26c43b47 JF |
125 | return; |
126 | } | |
889cb4f2 JF |
127 | if (!instance) |
128 | _class = object_getClass(_class); | |
26c43b47 JF |
129 | Method method = class_getInstanceMethod(_class, sel_getUid(oldname)); |
130 | if (method == nil) { | |
889cb4f2 JF |
131 | if (Debug_) |
132 | NSLog(@"WB:Warning: cannot find method [%s %s]", classname, oldname); | |
d5168fd6 | 133 | return; |
26c43b47 | 134 | } |
d5168fd6 JF |
135 | size_t namelen = strlen(oldname); |
136 | char newname[sizeof(WBPrefix) + namelen]; | |
137 | memcpy(newname, WBPrefix, sizeof(WBPrefix) - 1); | |
138 | memcpy(newname + sizeof(WBPrefix) - 1, oldname, namelen + 1); | |
d5168fd6 JF |
139 | const char *type = method_getTypeEncoding(method); |
140 | if (!class_addMethod(_class, sel_registerName(newname), method_getImplementation(method), type)) | |
2435118f | 141 | NSLog(@"WB:Error: failed to rename [%s %s]", classname, oldname); |
d5168fd6 JF |
142 | unsigned int count; |
143 | Method *methods = class_copyMethodList(_class, &count); | |
144 | for (unsigned int index(0); index != count; ++index) | |
145 | if (methods[index] == method) | |
146 | goto found; | |
147 | if (newimp != NULL) | |
148 | if (!class_addMethod(_class, sel_getUid(oldname), newimp, type)) | |
2435118f | 149 | NSLog(@"WB:Error: failed to rename [%s %s]", classname, oldname); |
d5168fd6 JF |
150 | goto done; |
151 | found: | |
152 | if (newimp != NULL) | |
153 | method_setImplementation(method, newimp); | |
154 | done: | |
155 | free(methods); | |
156 | } | |
d5168fd6 JF |
157 | /* }}} */ |
158 | ||
159 | @protocol WinterBoard | |
160 | - (NSString *) wb_pathForIcon; | |
161 | - (NSString *) wb_pathForResource:(NSString *)resource ofType:(NSString *)type; | |
08454e3a JF |
162 | - (id) wb_init; |
163 | - (id) wb_layer; | |
d5168fd6 | 164 | - (id) wb_initWithSize:(CGSize)size; |
889cb4f2 | 165 | - (id) wb_initWithSize:(CGSize)size label:(NSString *)label; |
62b2dbad | 166 | - (id) wb_initWithFrame:(CGRect)frame; |
26c43b47 | 167 | - (id) wb_initWithCoder:(NSCoder *)coder; |
62b2dbad | 168 | - (void) wb_setFrame:(CGRect)frame; |
ca13798d | 169 | - (void) wb_drawRect:(CGRect)rect; |
d5168fd6 | 170 | - (void) wb_setBackgroundColor:(id)color; |
62b2dbad | 171 | - (void) wb_setAlpha:(float)value; |
26c43b47 JF |
172 | - (void) wb_setBarStyle:(int)style; |
173 | - (id) wb_initWithFrame:(CGRect)frame withBarStyle:(int)style withTintColor:(UIColor *)color; | |
08454e3a | 174 | - (void) wb_setOpaque:(BOOL)opaque; |
889cb4f2 | 175 | - (void) wb_setInDock:(BOOL)docked; |
08454e3a | 176 | - (void) wb_didMoveToSuperview; |
889cb4f2 | 177 | + (UIImage *) wb_imageNamed:(NSString *)name inBundle:(NSBundle *)bundle; |
d5fb6e01 | 178 | + (UIImage *) wb_applicationImageNamed:(NSString *)name; |
394d1eb5 JF |
179 | - (NSDictionary *) wb_infoDictionary; |
180 | - (UIImage *) wb_icon; | |
d5fb6e01 JF |
181 | - (id) wb_initWithStatusBar:(id)bar mode:(int)mode; |
182 | - (id) wb_initWithMode:(int)mode orientation:(int)orientation; | |
183 | - (void) wb_setStatusBarMode:(int)mode orientation:(int)orientation duration:(float)duration fenceID:(int)id animation:(int)animation; | |
d5168fd6 JF |
184 | @end |
185 | ||
2435118f | 186 | NSMutableDictionary **ImageMap_; |
d5fb6e01 | 187 | NSMutableSet *UIImages_; |
2435118f | 188 | |
26c43b47 | 189 | NSFileManager *Manager_; |
889cb4f2 | 190 | NSDictionary *English_; |
d5fb6e01 JF |
191 | NSMutableDictionary *Info_; |
192 | NSMutableArray *themes_; | |
d5168fd6 | 193 | |
394d1eb5 | 194 | NSString *$pathForIcon$(SBApplication<WinterBoard> *self) { |
d5fb6e01 JF |
195 | for (NSString *theme in themes_) { |
196 | NSString *identifier = [self bundleIdentifier]; | |
197 | NSString *folder = [[self path] lastPathComponent]; | |
198 | NSString *dname = [self displayName]; | |
199 | NSString *didentifier = [self displayIdentifier]; | |
200 | ||
201 | if (Debug_) { | |
202 | NSLog(@"WB:Debug: [SBApplication(%@:%@:%@:%@) pathForIcon]", identifier, folder, dname, didentifier); | |
203 | } | |
62b2dbad | 204 | |
d5fb6e01 JF |
205 | #define testForIcon(Name) \ |
206 | if (NSString *name = Name) { \ | |
207 | NSString *path = [NSString stringWithFormat:@"%@/Icons/%@.png", theme, name]; \ | |
208 | if ([Manager_ fileExistsAtPath:path]) \ | |
209 | return path; \ | |
210 | } | |
211 | ||
212 | if (identifier != nil) { | |
213 | NSString *path = [NSString stringWithFormat:@"%@/Bundles/%@/icon.png", theme, identifier]; | |
214 | if ([Manager_ fileExistsAtPath:path]) | |
215 | return path; | |
26c43b47 | 216 | } |
889cb4f2 | 217 | |
d5fb6e01 JF |
218 | if (folder != nil) { |
219 | NSString *path = [NSString stringWithFormat:@"%@/Folders/%@/icon.png", theme, folder]; | |
220 | if ([Manager_ fileExistsAtPath:path]) | |
221 | return path; | |
222 | } | |
ca13798d | 223 | |
d5fb6e01 JF |
224 | testForIcon(identifier); |
225 | testForIcon(dname); | |
889cb4f2 | 226 | |
d5fb6e01 JF |
227 | if (didentifier != nil) { |
228 | testForIcon([English_ objectForKey:didentifier]); | |
889cb4f2 | 229 | |
d5fb6e01 JF |
230 | NSArray *parts = [didentifier componentsSeparatedByString:@"-"]; |
231 | if ([parts count] != 1) | |
232 | if (NSDictionary *english = [[[NSDictionary alloc] initWithContentsOfFile:[[self path] stringByAppendingString:@"/English.lproj/UIRoleDisplayNames.strings"]] autorelease]) | |
233 | testForIcon([english objectForKey:[parts lastObject]]); | |
234 | } | |
235 | } | |
889cb4f2 | 236 | |
394d1eb5 JF |
237 | return nil; |
238 | } | |
239 | ||
240 | static UIImage *SBApplicationIcon$icon(SBApplicationIcon<WinterBoard> *self, SEL sel) { | |
d5fb6e01 | 241 | if (![Info_ boolForKey:@"ComposeStoreIcons"]) |
56539693 JF |
242 | if (NSString *path = $pathForIcon$([self application])) |
243 | return [UIImage imageWithContentsOfFile:path]; | |
394d1eb5 JF |
244 | return [self wb_icon]; |
245 | } | |
246 | ||
247 | static NSString *SBApplication$pathForIcon(SBApplication<WinterBoard> *self, SEL sel) { | |
d5fb6e01 JF |
248 | if (NSString *path = $pathForIcon$(self)) |
249 | return path; | |
d5168fd6 JF |
250 | return [self wb_pathForIcon]; |
251 | } | |
252 | ||
394d1eb5 | 253 | static NSString *$pathForFile$inBundle$(NSString *file, NSBundle *bundle) { |
d5fb6e01 | 254 | for (NSString *theme in themes_) { |
ca13798d JF |
255 | NSString *identifier = [bundle bundleIdentifier]; |
256 | ||
257 | if (identifier != nil) { | |
d5fb6e01 JF |
258 | NSString *path = [NSString stringWithFormat:@"%@/Bundles/%@/%@", theme, identifier, file]; |
259 | if (Debug_) | |
260 | NSLog(@"WB:Debug:%@", path); | |
ca13798d JF |
261 | if ([Manager_ fileExistsAtPath:path]) |
262 | return path; | |
263 | } | |
264 | ||
265 | if (NSString *folder = [[bundle bundlePath] lastPathComponent]) { | |
d5fb6e01 JF |
266 | NSString *path = [NSString stringWithFormat:@"%@/Folders/%@/%@", theme, folder, file]; |
267 | if (Debug_) | |
268 | NSLog(@"WB:Debug:%@", path); | |
ca13798d JF |
269 | if ([Manager_ fileExistsAtPath:path]) |
270 | return path; | |
271 | } | |
26c43b47 | 272 | |
889cb4f2 JF |
273 | #define remapResourceName(oldname, newname) \ |
274 | else if ([file isEqualToString:oldname]) { \ | |
d5fb6e01 JF |
275 | NSString *path = [NSString stringWithFormat:@"%@/%@.png", theme, newname]; \ |
276 | if (Debug_) \ | |
277 | NSLog(@"WB:Debug:%@", path); \ | |
889cb4f2 JF |
278 | if ([Manager_ fileExistsAtPath:path]) \ |
279 | return path; \ | |
280 | } | |
26c43b47 | 281 | |
ca13798d | 282 | if (identifier == nil || ![identifier isEqualToString:@"com.apple.springboard"]); |
8e65ed9f JF |
283 | remapResourceName(@"FSO_BG.png", @"StatusBar") |
284 | remapResourceName(@"SBDockBG.png", @"Dock") | |
285 | remapResourceName(@"SBWeatherCelsius.png", @"Icons/Weather") | |
d5168fd6 JF |
286 | } |
287 | ||
889cb4f2 JF |
288 | return nil; |
289 | } | |
290 | ||
394d1eb5 | 291 | static UIImage *UIImage$imageNamed$inBundle$(Class<WinterBoard> self, SEL sel, NSString *name, NSBundle *bundle) { |
889cb4f2 JF |
292 | if (Debug_) |
293 | NSLog(@"WB:Debug: [UIImage(%@) imageNamed:\"%@\"]", [bundle bundleIdentifier], name); | |
ca13798d JF |
294 | if (NSString *path = $pathForFile$inBundle$(name, bundle)) |
295 | return [UIImage imageWithContentsOfFile:path]; | |
889cb4f2 JF |
296 | return [self wb_imageNamed:name inBundle:bundle]; |
297 | } | |
298 | ||
394d1eb5 | 299 | static UIImage *UIImage$imageNamed$(Class<WinterBoard> self, SEL sel, NSString *name) { |
889cb4f2 JF |
300 | return UIImage$imageNamed$inBundle$(self, sel, name, [NSBundle mainBundle]); |
301 | } | |
302 | ||
d5fb6e01 JF |
303 | static UIImage *UIImage$applicationImageNamed$(Class<WinterBoard> self, SEL sel, NSString *name) { |
304 | NSBundle *bundle = [NSBundle mainBundle]; | |
305 | if (Debug_) | |
306 | NSLog(@"WB:Debug: [UIImage(%@) applicationImageNamed:\"%@\"]", [bundle bundleIdentifier], name); | |
307 | if (NSString *path = $pathForFile$inBundle$(name, bundle)) | |
308 | return [UIImage imageWithContentsOfFile:path]; | |
309 | return [self wb_applicationImageNamed:name]; | |
310 | } | |
311 | ||
394d1eb5 | 312 | static NSString *NSBundle$pathForResource$ofType$(NSBundle<WinterBoard> *self, SEL sel, NSString *resource, NSString *type) { |
889cb4f2 JF |
313 | NSString *file = type == nil ? resource : [NSString stringWithFormat:@"%@.%@", resource, type]; |
314 | if (Debug_) | |
315 | NSLog(@"WB:Debug: [NSBundle(%@) pathForResource:\"%@\"]", [self bundleIdentifier], file); | |
ca13798d JF |
316 | if (NSString *path = $pathForFile$inBundle$(file, self)) |
317 | return path; | |
d5168fd6 JF |
318 | return [self wb_pathForResource:resource ofType:type]; |
319 | } | |
320 | ||
26c43b47 | 321 | bool UINavigationBar$setBarStyle$_(SBAppWindow<WinterBoard> *self) { |
d5fb6e01 JF |
322 | if (NSNumber *number = [Info_ objectForKey:@"NavigationBarStyle"]) { |
323 | [self wb_setBarStyle:[number intValue]]; | |
324 | return true; | |
26c43b47 JF |
325 | } |
326 | ||
327 | return false; | |
328 | } | |
329 | ||
330 | /*id UINavigationBarBackground$initWithFrame$withBarStyle$withTintColor$(UINavigationBarBackground<WinterBoard> *self, SEL sel, CGRect frame, int style, UIColor *tint) { | |
331 | _trace(); | |
332 | ||
d5fb6e01 JF |
333 | if (NSNumber *number = [Info_ objectForKey:@"NavigationBarStyle"]) |
334 | style = [number intValue]; | |
26c43b47 | 335 | |
d5fb6e01 JF |
336 | if (UIColor *color = [Info_ colorForKey:@"NavigationBarTint"]) |
337 | tint = color; | |
26c43b47 JF |
338 | |
339 | return [self wb_initWithFrame:frame withBarStyle:style withTintColor:tint]; | |
340 | }*/ | |
341 | ||
342 | /*id UINavigationBar$initWithCoder$(SBAppWindow<WinterBoard> *self, SEL sel, CGRect frame, NSCoder *coder) { | |
343 | self = [self wb_initWithCoder:coder]; | |
344 | if (self == nil) | |
345 | return nil; | |
346 | UINavigationBar$setBarStyle$_(self); | |
347 | return self; | |
348 | } | |
349 | ||
350 | id UINavigationBar$initWithFrame$(SBAppWindow<WinterBoard> *self, SEL sel, CGRect frame) { | |
351 | self = [self wb_initWithFrame:frame]; | |
352 | if (self == nil) | |
353 | return nil; | |
354 | UINavigationBar$setBarStyle$_(self); | |
355 | return self; | |
356 | }*/ | |
357 | ||
394d1eb5 | 358 | static void UINavigationBar$setBarStyle$(SBAppWindow<WinterBoard> *self, SEL sel, int style) { |
26c43b47 JF |
359 | if (UINavigationBar$setBarStyle$_(self)) |
360 | return; | |
361 | return [self wb_setBarStyle:style]; | |
362 | } | |
363 | ||
394d1eb5 | 364 | static void $didMoveToSuperview(SBButtonBar<WinterBoard> *self, SEL sel) { |
08454e3a JF |
365 | [[self superview] setBackgroundColor:[UIColor clearColor]]; |
366 | [self wb_didMoveToSuperview]; | |
367 | } | |
62b2dbad | 368 | |
d5fb6e01 JF |
369 | static NSString *$getTheme$(NSArray *files) { |
370 | for (NSString *theme in themes_) | |
371 | for (NSString *file in files) { | |
372 | NSString *path([NSString stringWithFormat:@"%@/%@", theme, file]); | |
373 | if ([Manager_ fileExistsAtPath:path]) | |
374 | return path; | |
375 | } | |
376 | ||
377 | return nil; | |
394d1eb5 JF |
378 | } |
379 | ||
380 | static id SBContentLayer$initWithSize$(SBContentLayer<WinterBoard> *self, SEL sel, CGSize size) { | |
d5168fd6 JF |
381 | self = [self wb_initWithSize:size]; |
382 | if (self == nil) | |
383 | return nil; | |
384 | ||
d5fb6e01 JF |
385 | if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.mp4"])) { |
386 | MPVideoView *video = [[[MPVideoView alloc] initWithFrame:[self bounds]] autorelease]; | |
387 | [video setMovieWithPath:path]; | |
388 | [video setRepeatMode:1]; | |
389 | [video setRepeatGap:0]; | |
390 | [self addSubview:video]; | |
391 | [video playFromBeginning];; | |
392 | } | |
393 | ||
394 | if (NSString *path = $getTheme$([NSArray arrayWithObjects:@"Wallpaper.png", @"Wallpaper.jpg", nil])) | |
394d1eb5 | 395 | if (UIImage *image = [[[UIImage alloc] initWithContentsOfFile:path] autorelease]) |
62b2dbad | 396 | [self addSubview:[[[UIImageView alloc] initWithImage:image] autorelease]]; |
d5fb6e01 JF |
397 | |
398 | if (NSString *path = $getTheme$([NSArray arrayWithObject:@"Wallpaper.html"])) { | |
394d1eb5 JF |
399 | CGRect bounds = [self bounds]; |
400 | ||
401 | UIWebDocumentView *view([[[UIWebDocumentView alloc] initWithFrame:bounds] autorelease]); | |
402 | [view setAutoresizes:YES]; | |
403 | ||
404 | [view loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]]; | |
405 | ||
406 | [[view webView] setDrawsBackground:NO]; | |
407 | [view setBackgroundColor:[UIColor clearColor]]; | |
408 | ||
409 | [self addSubview:view]; | |
08454e3a | 410 | } |
d5168fd6 JF |
411 | |
412 | return self; | |
413 | } | |
414 | ||
ca13798d JF |
415 | #define WBDelegate(delegate) \ |
416 | - (NSMethodSignature*) methodSignatureForSelector:(SEL)sel { \ | |
417 | if (Engineer_) \ | |
418 | NSLog(@"WB:MS:%s:(%s)", class_getName([self class]), sel_getName(sel)); \ | |
419 | if (NSMethodSignature *sig = [delegate methodSignatureForSelector:sel]) \ | |
420 | return sig; \ | |
421 | NSLog(@"WB:Error: [%s methodSignatureForSelector:(%s)]", class_getName([self class]), sel_getName(sel)); \ | |
422 | return nil; \ | |
423 | } \ | |
424 | \ | |
425 | - (void) forwardInvocation:(NSInvocation*)inv { \ | |
426 | SEL sel = [inv selector]; \ | |
427 | if ([delegate respondsToSelector:sel]) \ | |
428 | [inv invokeWithTarget:delegate]; \ | |
429 | else \ | |
430 | NSLog(@"WB:Error: [%s forwardInvocation:(%s)]", class_getName([self class]), sel_getName(sel)); \ | |
431 | } | |
432 | ||
394d1eb5 JF |
433 | static unsigned *ContextCount_; |
434 | static void ***ContextStack_; | |
ca13798d JF |
435 | |
436 | extern "C" CGColorRef CGGStateGetSystemColor(void *); | |
437 | extern "C" CGColorRef CGGStateGetFillColor(void *); | |
438 | extern "C" CGColorRef CGGStateGetStrokeColor(void *); | |
439 | extern "C" NSString *UIStyleStringFromColor(CGColorRef); | |
440 | ||
441 | @interface WBTime : NSProxy { | |
442 | NSString *time_; | |
443 | _transient SBStatusBarTimeView *view_; | |
889cb4f2 JF |
444 | } |
445 | ||
ca13798d | 446 | - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view; |
889cb4f2 JF |
447 | |
448 | @end | |
449 | ||
ca13798d | 450 | @implementation WBTime |
889cb4f2 JF |
451 | |
452 | - (void) dealloc { | |
ca13798d | 453 | [time_ release]; |
889cb4f2 JF |
454 | [super dealloc]; |
455 | } | |
456 | ||
ca13798d JF |
457 | - (id) initWithTime:(NSString *)time view:(SBStatusBarTimeView *)view { |
458 | time_ = [time retain]; | |
459 | view_ = view; | |
889cb4f2 JF |
460 | return self; |
461 | } | |
462 | ||
ca13798d JF |
463 | WBDelegate(time_) |
464 | ||
465 | - (CGSize) drawAtPoint:(CGPoint)point forWidth:(float)width withFont:(UIFont *)font lineBreakMode:(int)mode { | |
d5fb6e01 JF |
466 | if (NSString *custom = [Info_ objectForKey:@"TimeStyle"]) { |
467 | BOOL mode; | |
468 | object_getInstanceVariable(view_, "_mode", (void **) &mode); | |
469 | ||
470 | [time_ drawAtPoint:point withStyle:[NSString stringWithFormat:@"" | |
471 | "font-family: Helvetica; " | |
472 | "font-weight: bold; " | |
473 | "font-size: 14px; " | |
474 | "color: %@; " | |
475 | "%@", mode ? @"white" : @"black", custom]]; | |
476 | ||
477 | return CGSizeZero; | |
478 | } | |
ca13798d JF |
479 | |
480 | return [time_ drawAtPoint:point forWidth:width withFont:font lineBreakMode:mode]; | |
889cb4f2 JF |
481 | } |
482 | ||
ca13798d JF |
483 | @end |
484 | ||
485 | @interface WBIconLabel : NSProxy { | |
486 | NSString *string_; | |
487 | BOOL docked_; | |
889cb4f2 JF |
488 | } |
489 | ||
ca13798d JF |
490 | - (id) initWithString:(NSString *)string; |
491 | ||
492 | @end | |
493 | ||
494 | @implementation WBIconLabel | |
495 | ||
496 | - (void) dealloc { | |
497 | [string_ release]; | |
498 | [super dealloc]; | |
889cb4f2 JF |
499 | } |
500 | ||
ca13798d JF |
501 | - (id) initWithString:(NSString *)string { |
502 | string_ = [string retain]; | |
503 | return self; | |
504 | } | |
505 | ||
506 | WBDelegate(string_) | |
507 | ||
889cb4f2 | 508 | - (NSString *) _iconLabelStyle { |
d5fb6e01 JF |
509 | NSString *key = docked_ ? @"DockedIconLabelStyle" : @"UndockedIconLabelStyle"; |
510 | NSString *style = [Info_ objectForKey:key]; | |
511 | NSLog(@"WB:Debug:%@ = %@", key, style); | |
512 | return style; | |
889cb4f2 JF |
513 | } |
514 | ||
515 | - (CGSize) drawInRect:(CGRect)rect withFont:(UIFont *)font lineBreakMode:(int)mode alignment:(int)alignment { | |
d5fb6e01 | 516 | _trace(); |
889cb4f2 | 517 | if (NSString *custom = [self _iconLabelStyle]) { |
d5fb6e01 | 518 | NSString *style = [NSString stringWithFormat:@"" |
ca13798d JF |
519 | "font-family: Helvetica; " |
520 | "font-weight: bold; " | |
521 | "font-size: 11px; " | |
522 | "text-align: center; " | |
523 | "color: %@; " | |
d5fb6e01 | 524 | "%@", docked_ ? @"white" : @"#b3b3b3", custom]; |
ca13798d | 525 | |
d5fb6e01 JF |
526 | if (Debug_) |
527 | NSLog(@"WB:Debug:style = %@", style); | |
528 | [string_ drawInRect:rect withStyle:style]; | |
889cb4f2 JF |
529 | return CGSizeZero; |
530 | } | |
531 | ||
ca13798d | 532 | return [string_ drawInRect:rect withFont:font lineBreakMode:mode alignment:alignment]; |
889cb4f2 JF |
533 | } |
534 | ||
535 | - (void) drawInRect:(CGRect)rect withStyle:(NSString *)style { | |
d5fb6e01 JF |
536 | _trace(); |
537 | if (NSString *custom = [self _iconLabelStyle]) { | |
538 | NSString *combined = [NSString stringWithFormat:@"%@; %@", style, custom]; | |
539 | if (Debug_) | |
540 | NSLog(@"WB:Debug:combined = %@", combined); | |
541 | return [string_ drawInRect:rect withStyle:combined]; | |
542 | } | |
ca13798d JF |
543 | return [string_ drawInRect:rect withStyle:style]; |
544 | } | |
545 | ||
546 | - (BOOL) respondsToSelector:(SEL)sel { | |
547 | return | |
548 | sel == @selector(setInDock:) | |
549 | ? YES : [super respondsToSelector:sel]; | |
889cb4f2 JF |
550 | } |
551 | ||
552 | - (void) setInDock:(BOOL)docked { | |
553 | docked_ = docked; | |
554 | } | |
555 | ||
556 | @end | |
557 | ||
d5fb6e01 JF |
558 | static void SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$(SBStatusBarController<WinterBoard> *self, SEL sel, int mode, int orientation, float duration, int id, int animation) { |
559 | if (NSNumber *number = [Info_ objectForKey:@"StatusBarMode"]) | |
560 | mode = [number intValue]; | |
561 | return [self wb_setStatusBarMode:mode orientation:orientation duration:duration fenceID:id animation:animation]; | |
562 | } | |
563 | ||
564 | /*static id SBStatusBar$initWithMode$orientation$(SBStatusBar<WinterBoard> *self, SEL sel, int mode, int orientation) { | |
565 | return [self wb_initWithMode:mode orientation:orientation]; | |
566 | }*/ | |
567 | ||
568 | static id SBStatusBarContentsView$initWithStatusBar$mode$(SBStatusBarContentsView<WinterBoard> *self, SEL sel, id bar, int mode) { | |
569 | if (NSNumber *number = [Info_ objectForKey:@"StatusBarContentsMode"]) | |
570 | mode = [number intValue]; | |
571 | return [self wb_initWithStatusBar:bar mode:mode]; | |
572 | } | |
573 | ||
394d1eb5 | 574 | static void SBStatusBarTimeView$drawRect$(SBStatusBarTimeView<WinterBoard> *self, SEL sel, CGRect rect) { |
ca13798d JF |
575 | id time; |
576 | object_getInstanceVariable(self, "_time", (void **) &time); | |
577 | if (time != nil && [time class] != [WBTime class]) | |
578 | object_setInstanceVariable(self, "_time", (void *) [[WBTime alloc] initWithTime:[time autorelease] view:self]); | |
579 | return [self wb_drawRect:rect]; | |
580 | } | |
581 | ||
394d1eb5 | 582 | static void SBIconLabel$setInDock$(SBIconLabel<WinterBoard> *self, SEL sel, BOOL docked) { |
889cb4f2 JF |
583 | id label; |
584 | object_getInstanceVariable(self, "_label", (void **) &label); | |
d5fb6e01 | 585 | if (![Info_ boolForKey:@"UndockedIconLabels"]) |
889cb4f2 JF |
586 | docked = YES; |
587 | if (label != nil && [label respondsToSelector:@selector(setInDock:)]) | |
588 | [label setInDock:docked]; | |
589 | return [self wb_setInDock:docked]; | |
590 | } | |
591 | ||
394d1eb5 | 592 | static id SBIconLabel$initWithSize$label$(SBIconLabel<WinterBoard> *self, SEL sel, CGSize size, NSString *label) { |
ca13798d JF |
593 | // XXX: technically I'm misusing self here |
594 | return [self wb_initWithSize:size label:[[[WBIconLabel alloc] initWithString:label] autorelease]]; | |
595 | //return [self wb_initWithSize:size label:label]; | |
889cb4f2 JF |
596 | } |
597 | ||
2435118f JF |
598 | extern "C" void FindMappedImages(void); |
599 | extern "C" NSData *UIImagePNGRepresentation(UIImage *); | |
600 | ||
394d1eb5 JF |
601 | static void (*__UISharedImageInitialize)(bool); |
602 | ||
d5168fd6 | 603 | extern "C" void WBInitialize() { |
2435118f | 604 | NSLog(@"WB:Notice: Installing WinterBoard..."); |
d5168fd6 JF |
605 | |
606 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | |
607 | ||
ca13798d | 608 | struct nlist nl[5]; |
2435118f | 609 | memset(nl, 0, sizeof(nl)); |
394d1eb5 | 610 | |
2435118f JF |
611 | nl[0].n_un.n_name = (char *) "___mappedImages"; |
612 | nl[1].n_un.n_name = (char *) "__UISharedImageInitialize"; | |
ca13798d JF |
613 | nl[2].n_un.n_name = (char *) "___currentContextCount"; |
614 | nl[3].n_un.n_name = (char *) "___currentContextStack"; | |
394d1eb5 | 615 | |
2435118f | 616 | nlist("/System/Library/Frameworks/UIKit.framework/UIKit", nl); |
394d1eb5 | 617 | |
2435118f | 618 | ImageMap_ = (id *) nl[0].n_value; |
394d1eb5 | 619 | __UISharedImageInitialize = (void (*)(bool)) nl[1].n_value; |
ca13798d JF |
620 | ContextCount_ = (unsigned *) nl[2].n_value; |
621 | ContextStack_ = (void ***) nl[3].n_value; | |
2435118f JF |
622 | |
623 | __UISharedImageInitialize(false); | |
624 | ||
889cb4f2 JF |
625 | English_ = [[NSDictionary alloc] initWithContentsOfFile:@"/System/Library/CoreServices/SpringBoard.app/English.lproj/LocalizedApplicationNames.strings"]; |
626 | if (English_ != nil) | |
627 | English_ = [English_ retain]; | |
628 | ||
2435118f | 629 | Manager_ = [[NSFileManager defaultManager] retain]; |
d5fb6e01 | 630 | UIImages_ = [[NSMutableSet alloc] initWithCapacity:16]; |
2435118f | 631 | |
26c43b47 | 632 | //WBRename("UINavigationBar", "initWithCoder:", (IMP) &UINavigationBar$initWithCoder$); |
889cb4f2 | 633 | WBRename(true, "UINavigationBar", "setBarStyle:", (IMP) &UINavigationBar$setBarStyle$); |
26c43b47 | 634 | //WBRename("UINavigationBarBackground", "initWithFrame:withBarStyle:withTintColor:", (IMP) &UINavigationBarBackground$initWithFrame$withBarStyle$withTintColor$); |
08454e3a | 635 | |
889cb4f2 JF |
636 | WBRename(false, "UIImage", "imageNamed:inBundle:", (IMP) &UIImage$imageNamed$inBundle$); |
637 | WBRename(false, "UIImage", "imageNamed:", (IMP) &UIImage$imageNamed$); | |
d5fb6e01 | 638 | WBRename(false, "UIImage", "applicationImageNamed:", (IMP) &UIImage$applicationImageNamed$); |
394d1eb5 | 639 | WBRename(true, "SBApplicationIcon", "icon", (IMP) &SBApplicationIcon$icon); |
889cb4f2 JF |
640 | WBRename(true, "SBApplication", "pathForIcon", (IMP) &SBApplication$pathForIcon); |
641 | WBRename(true, "NSBundle", "pathForResource:ofType:", (IMP) &NSBundle$pathForResource$ofType$); | |
642 | WBRename(true, "SBContentLayer", "initWithSize:", (IMP) &SBContentLayer$initWithSize$); | |
d5fb6e01 JF |
643 | WBRename(true, "SBStatusBarContentsView", "initWithStatusBar:mode:", (IMP) &SBStatusBarContentsView$initWithStatusBar$mode$); |
644 | //WBRename(true, "SBStatusBar", "initWithMode:orientation:", (IMP) &SBStatusBar$initWithMode$orientation$); | |
889cb4f2 JF |
645 | WBRename(true, "SBStatusBarContentsView", "didMoveToSuperview", (IMP) &$didMoveToSuperview); |
646 | WBRename(true, "SBButtonBar", "didMoveToSuperview", (IMP) &$didMoveToSuperview); | |
647 | WBRename(true, "SBIconLabel", "setInDock:", (IMP) &SBIconLabel$setInDock$); | |
648 | WBRename(true, "SBIconLabel", "initWithSize:label:", (IMP) &SBIconLabel$initWithSize$label$); | |
ca13798d | 649 | WBRename(true, "SBStatusBarTimeView", "drawRect:", (IMP) &SBStatusBarTimeView$drawRect$); |
d5fb6e01 JF |
650 | WBRename(true, "SBStatusBarController", "setStatusBarMode:orientation:duration:fenceID:animation:", (IMP) &SBStatusBarController$setStatusBarMode$orientation$duration$fenceID$animation$); |
651 | ||
652 | themes_ = [[NSMutableArray alloc] initWithCapacity:8]; | |
26c43b47 | 653 | |
d5fb6e01 | 654 | if (NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"/User/Library/Preferences/com.saurik.WinterBoard.plist"]]) { |
62b2dbad | 655 | [settings autorelease]; |
62b2dbad | 656 | |
d5fb6e01 JF |
657 | NSArray *themes = [settings objectForKey:@"Themes"]; |
658 | if (themes == nil) | |
659 | if (NSString *theme = [settings objectForKey:@"Theme"]) | |
660 | themes = [NSArray arrayWithObject:[NSDictionary dictionaryWithObjectsAndKeys: | |
661 | theme, @"Name", | |
662 | [NSNumber numberWithBool:YES], @"Active", | |
663 | nil]]; | |
664 | if (themes != nil) | |
665 | for (NSDictionary *theme in themes) { | |
666 | NSNumber *active = [theme objectForKey:@"Active"]; | |
667 | if (![active boolValue]) | |
668 | continue; | |
26c43b47 | 669 | |
d5fb6e01 JF |
670 | NSString *name = [theme objectForKey:@"Name"]; |
671 | if (name == nil) | |
672 | continue; | |
673 | ||
674 | NSString *theme = nil; | |
675 | ||
676 | if (theme == nil) { | |
677 | NSString *path = [NSString stringWithFormat:@"/Library/Themes/%@.theme", name]; | |
678 | if ([Manager_ fileExistsAtPath:path]) { | |
679 | [themes_ addObject:path]; | |
680 | continue; | |
681 | } | |
682 | } | |
683 | ||
684 | if (theme == nil) { | |
685 | NSString *path = [NSString stringWithFormat:@"/Library/Themes/%@", name]; | |
686 | if ([Manager_ fileExistsAtPath:path]) { | |
687 | [themes_ addObject:path]; | |
688 | continue; | |
689 | } | |
690 | } | |
691 | ||
692 | if (theme == nil) { | |
693 | NSString *path = [NSString stringWithFormat:@"%@/Library/SummerBoard/Themes/%@", NSHomeDirectory(), name]; | |
694 | if ([Manager_ fileExistsAtPath:path]) { | |
695 | [themes_ addObject:path]; | |
696 | continue; | |
697 | } | |
698 | } | |
699 | } | |
26c43b47 JF |
700 | } |
701 | ||
d5fb6e01 JF |
702 | Info_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain]; |
703 | ||
704 | for (NSString *theme in themes_) { | |
705 | NSString *folder = [NSString stringWithFormat:@"%@/UIImages", theme]; | |
2435118f JF |
706 | if (NSArray *images = [Manager_ contentsOfDirectoryAtPath:folder error:NULL]) |
707 | for (int i(0), e = [images count]; i != e; ++i) { | |
708 | NSString *name = [images objectAtIndex:i]; | |
709 | if (![name hasSuffix:@".png"]) | |
710 | continue; | |
d5fb6e01 JF |
711 | if ([UIImages_ containsObject:name]) |
712 | continue; | |
2435118f JF |
713 | NSString *path = [NSString stringWithFormat:@"%@/%@", folder, name]; |
714 | UIImage *image = [UIImage imageWithContentsOfFile:path]; | |
715 | [*ImageMap_ setObject:(id)[image imageRef] forKey:name]; | |
d5fb6e01 | 716 | [UIImages_ addObject:name]; |
2435118f JF |
717 | } |
718 | ||
d5fb6e01 JF |
719 | if (NSDictionary *info = [[NSDictionary alloc] initWithContentsOfFile:[NSString stringWithFormat:@"%@/Info.plist", theme]]) |
720 | for (NSString *key in [info allKeys]) | |
721 | if ([Info_ objectForKey:key] == nil) | |
722 | [Info_ setObject:[info objectForKey:key] forKey:key]; | |
62b2dbad JF |
723 | } |
724 | ||
d5fb6e01 JF |
725 | if (Debug_) |
726 | NSLog(@"WB:Debug:Info = %@", [Info_ description]); | |
727 | ||
d5168fd6 JF |
728 | [pool release]; |
729 | } |