#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/mount.h>
+#include <sys/reboot.h>
#include <fcntl.h>
#include <notify.h>
#include <Cytore.hpp>
#include "UICaboodle/BrowserView.h"
+#include "SDURLCache/SDURLCache.h"
#include "substrate.h"
/* }}} */
}
- (id) yieldToSelector:(SEL)selector withObject:(id)object {
- /*return [self performSelector:selector withObject:object];*/
-
volatile bool stopped(false);
NSMutableArray *context([NSMutableArray arrayWithObjects:
/* Random Global Variables {{{ */
static const int PulseInterval_ = 50000;
-static const int ButtonBarWidth_ = 60;
-static const int ButtonBarHeight_ = 48;
-static const float KeyboardTime_ = 0.3f;
static int Finish_;
static NSArray *Finishes_;
@class PackageController;
@protocol CydiaDelegate
+- (void) retainNetworkActivityIndicator;
+- (void) releaseNetworkActivityIndicator;
- (void) setPackageController:(PackageController *)view;
- (void) clearPackage:(Package *)package;
- (void) installPackage:(Package *)package;
- (void) syncData;
- (void) showSettings;
- (UIProgressHUD *) addProgressHUD;
-- (BOOL) hudIsShowing;
- (void) removeProgressHUD:(UIProgressHUD *)hud;
- (CYViewController *) pageForPackage:(NSString *)name;
- (PackageController *) packageController;
- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
@end
+
+static id<CydiaDelegate> CydiaApp;
/* }}} */
/* Status Delegation {{{ */
struct MetaValue :
Cytore::Block
{
+ uint32_t active_;
Cytore::Offset<PackageValue> packages_[1 << 16];
};
NSDictionary *package((NSDictionary *) value);
if (NSNumber *subscribed = [package objectForKey:@"IsSubscribed"])
- if ([subscribed boolValue])
+ if ([subscribed boolValue] && !metadata->subscribed_)
metadata->subscribed_ = true;
if (NSDate *date = [package objectForKey:@"FirstSeen"]) {
metadata->first_ = time;
}
- if (NSDate *date = [package objectForKey:@"LastSeen"]) {
+ NSDate *date([package objectForKey:@"LastSeen"]);
+ NSString *version([package objectForKey:@"LastVersion"]);
+
+ if (date != nil && version != nil) {
time_t time([date timeIntervalSince1970]);
- if (metadata->last_ < time || metadata->last_ == 0) {
- metadata->last_ = time;
- goto last;
- }
- } else if (metadata->last_ == 0) last: {
- NSString *version([package objectForKey:@"LastVersion"]);
- if (CFStringGetCString((CFStringRef) version, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
- size_t length(strlen(buffer));
- uint16_t vhash(hashlittle(buffer, length));
+ if (metadata->last_ < time || metadata->last_ == 0)
+ if (CFStringGetCString((CFStringRef) version, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
+ size_t length(strlen(buffer));
+ uint16_t vhash(hashlittle(buffer, length));
- size_t capped(std::min<size_t>(8, length));
- char *latest(buffer + length - capped);
+ size_t capped(std::min<size_t>(8, length));
+ char *latest(buffer + length - capped);
- strncpy(metadata->version_, latest, sizeof(metadata->version_));
- metadata->vhash_ = vhash;
- }
+ strncpy(metadata->version_, latest, sizeof(metadata->version_));
+ metadata->vhash_ = vhash;
+
+ metadata->last_ = time;
+ }
}
}
// }}}
return _not(uint32_t) - value.key;
}
-_finline static void Stifle(uint8_t &value) {
-}
-
uint32_t PackagePrefixRadix(Package *self, void *context) {
size_t offset(reinterpret_cast<size_t>(context));
CYString &name([self cyname]);
uint8_t data[4];
- // 0.607997
-
if (offset == 0 && zeros != 0) {
memset(data, '0', zeros);
memcpy(data + zeros, text, 4 - zeros);
_end
_profile(Package$initWithVersion$Cache)
- id_.set(NULL, iterator.Name());
name_.set(NULL, iterator.Display());
latest_.set(NULL, StripVersion_(version_.VerStr()));
installed_.set(NULL, StripVersion_(current.VerStr()));
_end
- _profile(Package$initWithVersion$Lower)
- // XXX: do not use tolower() as this is not locale-specific? :(
- char *data(id_.data());
- for (size_t i(0), e(id_.size()); i != e; ++i)
- if ((data[i] & 0x20) == 0) {
- id_.copy(pool);
- data = id_.data();
- for (; i != e; ++i)
- data[i] |= 0x20;
- break;
- }
- _end
-
_profile(Package$initWithVersion$Tags)
pkgCache::TagIterator tag(iterator.TagList());
if (!tag.end()) {
_end
_profile(Package$initWithVersion$Metadata)
- PackageValue *metadata(PackageFind(id_.data(), id_.size()));
+ const char *mixed(iterator.Name());
+ size_t size(strlen(mixed));
+ char lower[size + 1];
+
+ for (size_t i(0); i != size; ++i)
+ lower[i] = mixed[i] | 0x20;
+ lower[size] = '\0';
+
+ PackageValue *metadata(PackageFind(lower, size));
metadata_ = metadata;
+ id_.set(NULL, metadata->name_, size);
+
const char *latest(version_.VerStr());
size_t length(strlen(latest));
metadata->first_ = now_;
if (metadata->vhash_ != vhash || strncmp(metadata->version_, latest, sizeof(metadata->version_)) != 0) {
- metadata->last_ = now_;
strncpy(metadata->version_, latest, sizeof(metadata->version_));
metadata->vhash_ = vhash;
+ metadata->last_ = now_;
} else if (metadata->last_ == 0)
metadata->last_ = metadata->first_;
_end
zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
apr_pool_create(&pool_, NULL);
- packages_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
+ size_t capacity(MetaFile_->active_);
+ if (capacity == 0)
+ capacity = 16384;
+ else
+ capacity += 1024;
+
+ packages_ = CFArrayCreateMutable(kCFAllocatorDefault, capacity, NULL);
int fds[2];
cache_.Close();
apr_pool_clear(pool_);
+
NSRecycleZone(zone_);
+ zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
int chk(creat("/tmp/cydia.chk", 0644));
if (chk != -1)
_trace();
size_t count(CFArrayGetCount(packages_));
+ MetaFile_->active_ = count;
+
for (size_t index(0); index != count; ++index)
[(Package *) CFArrayGetValueAtIndex(packages_, index) setIndex:index];
- (void) configure {
NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
+ _trace();
system([dpkg UTF8String]);
+ _trace();
}
- (bool) clean {
return;
}
+ [CydiaApp retainNetworkActivityIndicator];
+
bool failed = false;
for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
];
}
+ [CydiaApp releaseNetworkActivityIndicator];
+
if (failed) {
_trace();
return;
break;
case 2:
- system("launchctl stop com.apple.SpringBoard");
- break;
+ _trace();
+ goto reload;
case 3:
- system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
+ _trace();
+ goto reload;
+
+ reload:
+ system("/usr/bin/sbreload");
+ _trace();
break;
case 4:
- system("reboot");
+ _trace();
+ if (void (*SBReboot)(mach_port_t) = reinterpret_cast<void (*)(mach_port_t)>(dlsym(RTLD_DEFAULT, "SBReboot")))
+ SBReboot(SBSSpringBoardServerPort());
+ else
+ reboot2(RB_AUTOBOOT);
break;
}
}
case 4: [close_ setTitle:UCLocalize("REBOOT_DEVICE")]; break;
}
+ _trace();
system("su -c /usr/bin/uicache mobile");
+ _trace();
UpdateExternalStatus(Finish_ == 0 ? 2 : 0);
- (void) _addProgressOutput:(NSString *)output {
[output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
CGSize size = [output_ contentSize];
- CGRect rect = {{0, size.height}, {size.width, 0}};
- [output_ scrollRectToVisible:rect animated:YES];
+ CGPoint offset = [output_ contentOffset];
+ if (size.height - offset.y < [output_ frame].size.height + 20.f) {
+ CGRect rect = {{0, size.height-1}, {size.width, 1}};
+ [output_ scrollRectToVisible:rect animated:YES];
+ }
}
- (BOOL) isRunning {
}
if (badge_ != nil) {
- CGSize size = [badge_ size];
+ CGRect rect;
+ rect.size = [badge_ size];
+
+ rect.size.width /= 2;
+ rect.size.height /= 2;
+
+ rect.origin.x = 36 - rect.size.width / 2;
+ rect.origin.y = 36 - rect.size.height / 2;
- [badge_ drawAtPoint:CGPointMake(
- 36 - size.width / 2,
- 36 - size.height / 2
- )];
+ [badge_ drawInRect:rect];
}
if (highlighted)
UITableViewDelegate
> {
_transient Database *database_;
+ unsigned era_;
NSMutableArray *packages_;
NSMutableArray *sections_;
UITableView *list_;
}
- (Package *) packageAtIndexPath:(NSIndexPath *)path {
+@synchronized (database_) {
+ if ([database_ era] != era_)
+ return nil;
+
Section *section([sections_ objectAtIndex:[path section]]);
NSInteger row([path row]);
Package *package([packages_ objectAtIndex:([section row] + row)]);
- return package;
-}
+ return [[package retain] autorelease];
+} }
- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
}
- (void) reloadData {
+ era_ = [database_ era];
NSArray *packages = [database_ packages];
[packages_ removeAllObjects];
- (void) positionViews {
CGRect frame = [cancel_ frame];
+ frame.size = [cancel_ sizeThatFits:frame.size];
frame.origin.x = [self frame].size.width - frame.size.width - 5;
frame.origin.y = ([self frame].size.height - frame.size.height) / 2;
[cancel_ setFrame:frame];
if ((self = [super initWithFrame:frame])) {
[self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
- [self setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
[self setBarStyle:UIBarStyleBlack];
UIBarStyle barstyle([self _barStyle:NO]);
@class CYNavigationController;
/* Cydia Tab Bar Controller {{{ */
-@interface CYTabBarController : UITabBarController {
+@interface CYTabBarController : UITabBarController <
+ ProgressDelegate
+> {
_transient Database *database_;
+ RefreshBar *refreshbar_;
+
+ bool dropped_;
+ bool updating_;
+ // XXX: ok, "updatedelegate_"?...
+ _transient NSObject<CydiaDelegate> *updatedelegate_;
+
+ id root_;
}
+- (void) dropBar:(BOOL)animated;
+- (void) beginUpdate;
+- (void) raiseBar:(BOOL)animated;
+- (BOOL) updating;
+
@end
@implementation CYTabBarController
- (id) initWithDatabase:(Database *)database {
if ((self = [super init]) != nil) {
database_ = database;
+
+ [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
+
+ refreshbar_ = [[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self];
} return self;
}
-@end
-/* }}} */
+- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
+ return IsWildcat_ || orientation == UIInterfaceOrientationPortrait;
+}
-/* Cydia Navigation Controller {{{ */
-@interface CYNavigationController : UINavigationController {
- _transient Database *database_;
- _transient id<UINavigationControllerDelegate> delegate_;
+- (void) setUpdate:(NSDate *)date {
+ [self beginUpdate];
}
-- (id) initWithDatabase:(Database *)database;
-- (void) reloadData;
+- (void) beginUpdate {
+ [refreshbar_ start];
+ [self dropBar:YES];
-@end
+ [updatedelegate_ retainNetworkActivityIndicator];
+ updating_ = true;
+ [NSThread
+ detachNewThreadSelector:@selector(performUpdate)
+ toTarget:self
+ withObject:nil
+ ];
+}
-@implementation CYNavigationController
+- (void) performUpdate { _pooled
+ Status status;
+ status.setDelegate(self);
+ [database_ updateWithStatus:status];
-- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
- // Inherit autorotation settings for modal parents.
- if ([self parentViewController] && [[self parentViewController] modalViewController] == self) {
- return [[self parentViewController] shouldAutorotateToInterfaceOrientation:orientation];
- } else {
- return [super shouldAutorotateToInterfaceOrientation:orientation];
- }
+ [self
+ performSelectorOnMainThread:@selector(completeUpdate)
+ withObject:nil
+ waitUntilDone:NO
+ ];
}
-- (void) dealloc {
- [super dealloc];
+- (void) stopUpdateWithSelector:(SEL)selector {
+ updating_ = false;
+ [updatedelegate_ releaseNetworkActivityIndicator];
+
+ [self raiseBar:YES];
+ [refreshbar_ stop];
+
+ [updatedelegate_ performSelector:selector withObject:nil afterDelay:0];
}
-- (void) reloadData {
- size_t count([[self viewControllers] count]);
- for (size_t i(0); i != count; ++i) {
- CYViewController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
- [page reloadData];
- }
+- (void) completeUpdate {
+ if (!updating_)
+ return;
+ [self stopUpdateWithSelector:@selector(reloadData)];
}
-- (void) setDelegate:(id<UINavigationControllerDelegate>)delegate {
- delegate_ = delegate;
+- (void) cancelUpdate {
+ [self stopUpdateWithSelector:@selector(updateData)];
}
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super init]) != nil) {
- database_ = database;
- } return self;
+- (void) cancelPressed {
+ [self cancelUpdate];
}
-@end
-/* }}} */
-/* Cydia:// Protocol {{{ */
-@interface CydiaURLProtocol : NSURLProtocol {
+- (BOOL) updating {
+ return updating_;
}
-@end
+- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
+ [refreshbar_ setPrompt:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]];
+}
-@implementation CydiaURLProtocol
+- (void) startProgress {
+}
-+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
- NSURL *url([request URL]);
- if (url == nil)
- return NO;
- NSString *scheme([[url scheme] lowercaseString]);
- if (scheme == nil || ![scheme isEqualToString:@"cydia"])
- return NO;
- return YES;
+- (void) setProgressTitle:(NSString *)title {
+ [self
+ performSelectorOnMainThread:@selector(_setProgressTitle:)
+ withObject:title
+ waitUntilDone:YES
+ ];
}
-+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
- return request;
+- (bool) isCancelling:(size_t)received {
+ return !updating_;
}
-- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
- id<NSURLProtocolClient> client([self client]);
- if (icon == nil)
- [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
- else {
- NSData *data(UIImagePNGRepresentation(icon));
+- (void) setProgressPercent:(float)percent {
+ [self
+ performSelectorOnMainThread:@selector(_setProgressPercent:)
+ withObject:[NSNumber numberWithFloat:percent]
+ waitUntilDone:YES
+ ];
+}
- NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
- [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
- [client URLProtocol:self didLoadData:data];
- [client URLProtocolDidFinishLoading:self];
- }
+- (void) addProgressOutput:(NSString *)output {
+ [self
+ performSelectorOnMainThread:@selector(_addProgressOutput:)
+ withObject:output
+ waitUntilDone:YES
+ ];
}
-- (void) startLoading {
- id<NSURLProtocolClient> client([self client]);
- NSURLRequest *request([self request]);
+- (void) _setProgressTitle:(NSString *)title {
+ [refreshbar_ setPrompt:title];
+}
- NSURL *url([request URL]);
- NSString *href([url absoluteString]);
+- (void) _setProgressPercent:(NSNumber *)percent {
+ [refreshbar_ setProgress:[percent floatValue]];
+}
- NSString *path([href substringFromIndex:8]);
- NSRange slash([path rangeOfString:@"/"]);
+- (void) _addProgressOutput:(NSString *)output {
+}
- NSString *command;
- if (slash.location == NSNotFound) {
- command = path;
- path = nil;
+- (void) setUpdateDelegate:(id)delegate {
+ updatedelegate_ = delegate;
+}
+
+- (CGFloat) statusBarHeight {
+ if (UIInterfaceOrientationIsPortrait([self interfaceOrientation])) {
+ return [[UIApplication sharedApplication] statusBarFrame].size.height;
} else {
- command = [path substringToIndex:slash.location];
- path = [path substringFromIndex:(slash.location + 1)];
+ return [[UIApplication sharedApplication] statusBarFrame].size.width;
}
+}
- Database *database([Database sharedInstance]);
+- (UIView *) transitionView {
+ if ([self respondsToSelector:@selector(_transitionView)])
+ return [self _transitionView];
+ else
+ return MSHookIvar<id>(self, "_viewControllerTransitionView");
+}
- if ([command isEqualToString:@"package-icon"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- Package *package([database packageWithName:path]);
- if (package == nil)
- goto fail;
- UIImage *icon([package icon]);
- [self _returnPNGWithImage:icon forRequest:request];
- } else if ([command isEqualToString:@"source-icon"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSString *source(Simplify(path));
- UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
- if (icon == nil)
- icon = [UIImage applicationImageNamed:@"unknown.png"];
- [self _returnPNGWithImage:icon forRequest:request];
- } else if ([command isEqualToString:@"uikit-image"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- UIImage *icon(_UIImageWithName(path));
- [self _returnPNGWithImage:icon forRequest:request];
- } else if ([command isEqualToString:@"section-icon"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSString *section(Simplify(path));
- UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
+- (void) dropBar:(BOOL)animated {
+ if (dropped_)
+ return;
+ dropped_ = true;
+
+ UIView *transition([self transitionView]);
+ [[self view] addSubview:refreshbar_];
+
+ CGRect barframe([refreshbar_ frame]);
+
+ if (false) // XXX: _UIApplicationLinkedOnOrAfter(4)
+ barframe.origin.y = [self statusBarHeight];
+ else
+ barframe.origin.y = 0;
+
+ [refreshbar_ setFrame:barframe];
+
+ if (animated)
+ [UIView beginAnimations:nil context:NULL];
+
+ CGRect viewframe = [transition frame];
+ viewframe.origin.y += barframe.size.height;
+ viewframe.size.height -= barframe.size.height;
+ [transition setFrame:viewframe];
+
+ if (animated)
+ [UIView commitAnimations];
+
+ // Ensure bar has the proper width for our view, it might have changed
+ barframe.size.width = viewframe.size.width;
+ [refreshbar_ setFrame:barframe];
+
+ // XXX: fix Apple's layout bug
+ [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+- (void) raiseBar:(BOOL)animated {
+ if (!dropped_)
+ return;
+ dropped_ = false;
+
+ UIView *transition([self transitionView]);
+ [refreshbar_ removeFromSuperview];
+
+ CGRect barframe([refreshbar_ frame]);
+
+ if (animated)
+ [UIView beginAnimations:nil context:NULL];
+
+ CGRect viewframe = [transition frame];
+ viewframe.origin.y -= barframe.size.height;
+ viewframe.size.height += barframe.size.height;
+ [transition setFrame:viewframe];
+
+ if (animated)
+ [UIView commitAnimations];
+
+ // XXX: fix Apple's layout bug
+ // SRK [[self selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+#if 0
+- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
+ // XXX: fix Apple's layout bug
+ // SRK [[self selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+#endif
+
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+ bool dropped(dropped_);
+
+ if (dropped)
+ [self raiseBar:NO];
+
+ [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+
+ if (dropped)
+ [self dropBar:NO];
+
+ // XXX: fix Apple's layout bug
+ // SRK [[self selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+- (void) statusBarFrameChanged:(NSNotification *)notification {
+ if (dropped_) {
+ [self raiseBar:NO];
+ [self dropBar:NO];
+ }
+}
+
+- (void) dealloc {
+ [refreshbar_ release];
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
+}
+
+@end
+/* }}} */
+
+/* Cydia Navigation Controller {{{ */
+@interface CYNavigationController : UINavigationController {
+ _transient Database *database_;
+ _transient id<UINavigationControllerDelegate> delegate_;
+}
+
+- (id) initWithDatabase:(Database *)database;
+- (void) reloadData;
+
+@end
+
+
+@implementation CYNavigationController
+
+- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
+ // Inherit autorotation settings for modal parents.
+ if ([self parentViewController] && [[self parentViewController] modalViewController] == self) {
+ return [[self parentViewController] shouldAutorotateToInterfaceOrientation:orientation];
+ } else {
+ return [super shouldAutorotateToInterfaceOrientation:orientation];
+ }
+}
+
+- (void) dealloc {
+ [super dealloc];
+}
+
+- (void) reloadData {
+ size_t count([[self viewControllers] count]);
+ for (size_t i(0); i != count; ++i) {
+ CYViewController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
+ [page reloadData];
+ }
+}
+
+- (void) setDelegate:(id<UINavigationControllerDelegate>)delegate {
+ delegate_ = delegate;
+}
+
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super init]) != nil) {
+ database_ = database;
+ } return self;
+}
+
+@end
+/* }}} */
+/* Cydia:// Protocol {{{ */
+@interface CydiaURLProtocol : NSURLProtocol {
+}
+
+@end
+
+@implementation CydiaURLProtocol
+
++ (BOOL) canInitWithRequest:(NSURLRequest *)request {
+ NSURL *url([request URL]);
+ if (url == nil)
+ return NO;
+ NSString *scheme([[url scheme] lowercaseString]);
+ if (scheme == nil || ![scheme isEqualToString:@"cydia"])
+ return NO;
+ return YES;
+}
+
++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
+ return request;
+}
+
+- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
+ id<NSURLProtocolClient> client([self client]);
+ if (icon == nil)
+ [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
+ else {
+ NSData *data(UIImagePNGRepresentation(icon));
+
+ NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
+ [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [client URLProtocol:self didLoadData:data];
+ [client URLProtocolDidFinishLoading:self];
+ }
+}
+
+- (void) startLoading {
+ id<NSURLProtocolClient> client([self client]);
+ NSURLRequest *request([self request]);
+
+ NSURL *url([request URL]);
+ NSString *href([url absoluteString]);
+
+ NSString *path([href substringFromIndex:8]);
+ NSRange slash([path rangeOfString:@"/"]);
+
+ NSString *command;
+ if (slash.location == NSNotFound) {
+ command = path;
+ path = nil;
+ } else {
+ command = [path substringToIndex:slash.location];
+ path = [path substringFromIndex:(slash.location + 1)];
+ }
+
+ Database *database([Database sharedInstance]);
+
+ if ([command isEqualToString:@"package-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ Package *package([database packageWithName:path]);
+ if (package == nil)
+ goto fail;
+ UIImage *icon([package icon]);
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else if ([command isEqualToString:@"source-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ NSString *source(Simplify(path));
+ UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
+ if (icon == nil)
+ icon = [UIImage applicationImageNamed:@"unknown.png"];
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else if ([command isEqualToString:@"uikit-image"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ UIImage *icon(_UIImageWithName(path));
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else if ([command isEqualToString:@"section-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ NSString *section(Simplify(path));
+ UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
if (icon == nil)
icon = [UIImage applicationImageNamed:@"unknown.png"];
[self _returnPNGWithImage:icon forRequest:request];
UITableViewDelegate
> {
_transient Database *database_;
+ unsigned era_;
CFMutableArrayRef packages_;
NSMutableArray *sections_;
UITableView *list_;
}
- (Package *) packageAtIndexPath:(NSIndexPath *)path {
+@synchronized (database_) {
+ if ([database_ era] != era_)
+ return nil;
+
Section *section([sections_ objectAtIndex:[path section]]);
NSInteger row([path row]);
- return [self packageAtIndex:([section row] + row)];
-}
+ return [[[self packageAtIndex:([section row] + row)] retain] autorelease];
+} }
- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
}
- (void) reloadData {
+ era_ = [database_ era];
NSArray *packages = [database_ packages];
CFArrayRemoveAllValues(packages_);
search_ = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] bounds].size.width, 44.0f)];
[search_ layoutSubviews];
[search_ setPlaceholder:UCLocalize("SEARCH_EX")];
- UITextField *textField = [search_ searchField];
+
+ UITextField *textField;
+ if ([search_ respondsToSelector:@selector(searchField)])
+ textField = [search_ searchField];
+ else
+ textField = MSHookIvar<UITextField *>(search_, "_searchField");
+
[textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
[search_ setDelegate:self];
[textField setEnablesReturnKeyAutomatically:NO];
@end
/* }}} */
/* Settings Controller {{{ */
-@interface SettingsController : CYViewController <
+@interface CYPackageSettingsController : CYViewController <
UITableViewDataSource,
UITableViewDelegate
> {
@end
-@implementation SettingsController
+@implementation CYPackageSettingsController
- (void) dealloc {
[name_ release];
/* }}} */
/* Role Controller {{{ */
-@interface RoleController : CYViewController <
+@interface CYSettingsController : CYViewController <
UITableViewDataSource,
UITableViewDelegate
> {
@end
-@implementation RoleController
+@implementation CYSettingsController
- (void) dealloc {
[table_ release];
[segment_ release];
captrect.origin.x = 0;
captrect.origin.y = ([[self view] frame].size.height / 2) - (captrect.size.height * 2);
caption_ = [[[UILabel alloc] initWithFrame:captrect] autorelease];
- [caption_ setText:@"Initializing Filesystem"];
+ [caption_ setText:UCLocalize("PREPARING_FILESYSTEM")];
[caption_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
[caption_ setFont:[UIFont boldSystemFontOfSize:28.0f]];
[caption_ setTextColor:[UIColor whiteColor]];
statusrect.origin.y = ([[self view] frame].size.height / 2) - statusrect.size.height;
status_ = [[[UILabel alloc] initWithFrame:statusrect] autorelease];
[status_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
- [status_ setText:@"(Cydia will exit when complete.)"];
+ [status_ setText:UCLocalize("EXIT_WHEN_COMPLETE")];
[status_ setFont:[UIFont systemFontOfSize:16.0f]];
[status_ setTextColor:[UIColor whiteColor]];
[status_ setBackgroundColor:[UIColor clearColor]];
@end
/* }}} */
-/* Cydia Container {{{ */
-@interface CYContainer : UIViewController <ProgressDelegate> {
- _transient Database *database_;
- RefreshBar *refreshbar_;
-
- bool dropped_;
- bool updating_;
- // XXX: ok, "updatedelegate_"?...
- _transient NSObject<CydiaDelegate> *updatedelegate_;
- // XXX: can't we query for this variable when we need it?
- _transient UITabBarController *root_;
-}
-
-- (void) setTabBarController:(UITabBarController *)controller;
-
-- (void) dropBar:(BOOL)animated;
-- (void) beginUpdate;
-- (void) raiseBar:(BOOL)animated;
-- (BOOL) updating;
-
-@end
-
-@implementation CYContainer
-
-- (BOOL) _reallyWantsFullScreenLayout {
- return YES;
-}
-
-// NOTE: UIWindow only sends the top controller these messages,
-// So we have to forward them on.
-
-- (void) viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- [root_ viewDidAppear:animated];
-}
-
-- (void) viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
- [root_ viewWillAppear:animated];
-}
-
-- (void) viewDidDisappear:(BOOL)animated {
- [super viewDidDisappear:animated];
- [root_ viewDidDisappear:animated];
-}
-
-- (void) viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
- [root_ viewWillDisappear:animated];
-}
-
-- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
- return ![updatedelegate_ hudIsShowing] && (IsWildcat_ || orientation == UIInterfaceOrientationPortrait);
-}
-
-- (void) setTabBarController:(UITabBarController *)controller {
- root_ = controller;
- [[self view] addSubview:[root_ view]];
-}
-
-- (void) setUpdate:(NSDate *)date {
- [self beginUpdate];
-}
-
-- (void) beginUpdate {
- [self dropBar:YES];
- [refreshbar_ start];
-
- updating_ = true;
-
- [NSThread
- detachNewThreadSelector:@selector(performUpdate)
- toTarget:self
- withObject:nil
- ];
-}
-
-- (void) performUpdate { _pooled
- Status status;
- status.setDelegate(self);
- [database_ updateWithStatus:status];
-
- [self
- performSelectorOnMainThread:@selector(completeUpdate)
- withObject:nil
- waitUntilDone:NO
- ];
-}
-
-- (void) completeUpdate {
- if (!updating_)
- return;
- updating_ = false;
-
- [self raiseBar:YES];
- [refreshbar_ stop];
- [updatedelegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
-}
-
-- (void) cancelUpdate {
- updating_ = false;
- [self raiseBar:YES];
- [refreshbar_ stop];
- [updatedelegate_ performSelector:@selector(updateData) withObject:nil afterDelay:0];
-}
-
-- (void) cancelPressed {
- [self cancelUpdate];
-}
-
-- (BOOL) updating {
- return updating_;
-}
-
-- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
- [refreshbar_ setPrompt:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]];
-}
-
-- (void) startProgress {
-}
-
-- (void) setProgressTitle:(NSString *)title {
- [self
- performSelectorOnMainThread:@selector(_setProgressTitle:)
- withObject:title
- waitUntilDone:YES
- ];
-}
-
-- (bool) isCancelling:(size_t)received {
- return !updating_;
-}
-
-- (void) setProgressPercent:(float)percent {
- [self
- performSelectorOnMainThread:@selector(_setProgressPercent:)
- withObject:[NSNumber numberWithFloat:percent]
- waitUntilDone:YES
- ];
-}
-
-- (void) addProgressOutput:(NSString *)output {
- [self
- performSelectorOnMainThread:@selector(_addProgressOutput:)
- withObject:output
- waitUntilDone:YES
- ];
-}
-
-- (void) _setProgressTitle:(NSString *)title {
- [refreshbar_ setPrompt:title];
-}
-
-- (void) _setProgressPercent:(NSNumber *)percent {
- [refreshbar_ setProgress:[percent floatValue]];
-}
-
-- (void) _addProgressOutput:(NSString *)output {
-}
-
-- (void) setUpdateDelegate:(id)delegate {
- updatedelegate_ = delegate;
-}
-
-- (CGFloat) statusBarHeight {
- if (UIInterfaceOrientationIsPortrait([self interfaceOrientation])) {
- return [[UIApplication sharedApplication] statusBarFrame].size.height;
- } else {
- return [[UIApplication sharedApplication] statusBarFrame].size.width;
- }
-}
-
-- (void) dropBar:(BOOL)animated {
- if (dropped_)
- return;
- dropped_ = true;
-
- [[self view] addSubview:refreshbar_];
-
- CGFloat sboffset = [self statusBarHeight];
-
- CGRect barframe = [refreshbar_ frame];
- barframe.origin.y = sboffset;
- [refreshbar_ setFrame:barframe];
-
- if (animated)
- [UIView beginAnimations:nil context:NULL];
- CGRect viewframe = [[root_ view] frame];
- viewframe.origin.y += barframe.size.height + sboffset;
- viewframe.size.height -= barframe.size.height + sboffset;
- [[root_ view] setFrame:viewframe];
- if (animated)
- [UIView commitAnimations];
-
- // Ensure bar has the proper width for our view, it might have changed
- barframe.size.width = viewframe.size.width;
- [refreshbar_ setFrame:barframe];
-
- // XXX: fix Apple's layout bug
- [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
-}
-
-- (void) raiseBar:(BOOL)animated {
- if (!dropped_)
- return;
- dropped_ = false;
-
- [refreshbar_ removeFromSuperview];
-
- CGFloat sboffset = [self statusBarHeight];
-
- if (animated)
- [UIView beginAnimations:nil context:NULL];
- CGRect barframe = [refreshbar_ frame];
- CGRect viewframe = [[root_ view] frame];
- viewframe.origin.y -= barframe.size.height + sboffset;
- viewframe.size.height += barframe.size.height + sboffset;
- [[root_ view] setFrame:viewframe];
- if (animated)
- [UIView commitAnimations];
-
- // XXX: fix Apple's layout bug
- [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
-}
-
-- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
- // XXX: fix Apple's layout bug
- [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
-}
-
-- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
- if (dropped_) {
- [self raiseBar:NO];
- [self dropBar:NO];
- }
-
- // XXX: fix Apple's layout bug
- [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
-}
-
-- (void) statusBarFrameChanged:(NSNotification *)notification {
- if (dropped_) {
- [self raiseBar:NO];
- [self dropBar:NO];
- }
-}
-
-- (void) dealloc {
- [refreshbar_ release];
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [super dealloc];
-}
-
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super init]) != nil) {
- database_ = database;
-
- [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
-
- refreshbar_ = [[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self];
- } return self;
-}
-
-@end
-/* }}} */
-
typedef enum {
kCydiaTag = 0,
kSectionsTag = 1,
// XXX: evaluate all fields for _transient
UIWindow *window_;
- CYContainer *container_;
CYTabBarController *tabbar_;
NSMutableArray *essential_;
Database *database_;
- int tag_;
- int hudcount_;
NSURL *starturl_;
+ int tag_;
+
+ unsigned locked_;
+ unsigned activity_;
SectionsController *sections_;
ChangesController *changes_;
@implementation Cydia
- (void) beginUpdate {
- [container_ beginUpdate];
+ [tabbar_ beginUpdate];
}
- (BOOL) updating {
- return [container_ updating];
+ return [tabbar_ updating];
}
- (UIView *) rotatingContentViewForWindow:(UIWindow *)window {
// If we can reach the server, auto-refresh!
if (reachable)
- [container_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
+ [tabbar_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
[pool release];
}
}
}
+ NSLog(@"changes:#%u", changes);
+
UITabBarItem *changesItem = [[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem];
if (changes != 0) {
+ _trace();
NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
[changesItem setBadgeValue:badge];
[changesItem setAnimatedBadge:([essential_ count] > 0)];
-
- if ([self respondsToSelector:@selector(setApplicationBadge:)])
- [self setApplicationBadge:badge];
- else
- [self setApplicationBadgeString:badge];
+ [self setApplicationIconBadgeNumber:changes];
} else {
+ _trace();
[changesItem setBadgeValue:nil];
[changesItem setAnimatedBadge:NO];
-
- if ([self respondsToSelector:@selector(removeApplicationBadge)])
- [self removeApplicationBadge];
- else // XXX: maybe use setApplicationBadgeString also?
- [self setApplicationIconBadgeNumber:0];
+ [self setApplicationIconBadgeNumber:0];
}
[self _updateData];
CYNavigationController *navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
if (IsWildcat_)
[navigation setModalPresentationStyle:UIModalPresentationFormSheet];
- [container_ presentModalViewController:navigation animated:YES];
+ [tabbar_ presentModalViewController:navigation animated:YES];
[progress
detachNewThreadSelector:@selector(update_)
if (IsWildcat_)
[confirm_ setModalPresentationStyle:UIModalPresentationFormSheet];
- [container_ presentModalViewController:confirm_ animated:YES];
+ [tabbar_ presentModalViewController:confirm_ animated:YES];
return true;
}
navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
if (IsWildcat_)
[navigation setModalPresentationStyle:UIModalPresentationFormSheet];
- [container_ presentModalViewController:navigation animated:YES];
+ [tabbar_ presentModalViewController:navigation animated:YES];
}
[progress
withObject:nil
title:UCLocalize("RUNNING")
];
+
+ ++locked_;
}
- (void) progressControllerIsComplete:(ProgressController *)progress {
+ --locked_;
[self complete];
}
}
- (void) showSettings {
- RoleController *role = [[[RoleController alloc] initWithDatabase:database_ delegate:self] autorelease];
+ CYSettingsController *role = [[[CYSettingsController alloc] initWithDatabase:database_ delegate:self] autorelease];
CYNavigationController *nav = [[[CYNavigationController alloc] initWithRootViewController:role] autorelease];
if (IsWildcat_)
[nav setModalPresentationStyle:UIModalPresentationFormSheet];
- [container_ presentModalViewController:nav animated:YES];
+ [tabbar_ presentModalViewController:nav animated:YES];
+}
+
+- (void) retainNetworkActivityIndicator {
+ if (activity_++ == 0)
+ [self setNetworkActivityIndicatorVisible:YES];
+}
+
+- (void) releaseNetworkActivityIndicator {
+ if (--activity_ == 0)
+ [self setNetworkActivityIndicatorVisible:NO];
}
- (void) setPackageController:(PackageController *)view {
}
- (void) system:(NSString *)command { _pooled
+ _trace();
system([command UTF8String]);
+ _trace();
}
- (void) applicationWillSuspend {
[super applicationWillSuspend];
}
-- (BOOL) hudIsShowing {
- return (hudcount_ > 0);
-}
-
-- (void) applicationSuspend:(__GSEvent *)event {
+- (BOOL) isSafeToSuspend {
// Use external process status API internally.
// This is probably a really bad idea.
+ // XXX: what is the point of this? does this solve anything at all?
uint64_t status = 0;
int notify_token;
if (notify_register_check("com.saurik.Cydia.status", ¬ify_token) == NOTIFY_STATUS_OK) {
notify_cancel(notify_token);
}
- if (![self hudIsShowing] && status == 0)
+ return locked_ == 0 && status == 0;
+}
+
+- (void) applicationSuspend:(__GSEvent *)event {
+ if ([self isSafeToSuspend])
[super applicationSuspend:event];
}
- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
- if (![self hudIsShowing])
+ if ([self isSafeToSuspend])
[super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
}
- (void) _setSuspended:(BOOL)value {
- if (![self hudIsShowing])
+ if ([self isSafeToSuspend])
[super _setSuspended:value];
}
[window_ setUserInteractionEnabled:NO];
[hud show:YES];
- UIViewController *target = container_;
+ UIViewController *target = tabbar_;
while ([target modalViewController] != nil) target = [target modalViewController];
[[target view] addSubview:hud];
- hudcount_++;
+ ++locked_;
return hud;
}
[hud show:NO];
[hud removeFromSuperview];
[window_ setUserInteractionEnabled:YES];
- hudcount_--;
+ --locked_;
}
- (CYViewController *) pageForPackage:(NSString *)name {
else if ([path hasPrefix:@"/launch/"])
[self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
else if ([path hasPrefix:@"/package-settings/"])
- return [[[SettingsController alloc] initWithDatabase:database_ package:[path substringFromIndex:18]] autorelease];
+ return [[[CYPackageSettingsController alloc] initWithDatabase:database_ package:[path substringFromIndex:18]] autorelease];
else if ([path hasPrefix:@"/package-signature/"])
return [[[SignatureController alloc] initWithDatabase:database_ package:[path substringFromIndex:19]] autorelease];
else if ([path hasPrefix:@"/package/"])
- (void) applicationWillResignActive:(UIApplication *)application {
// Stop refreshing if you get a phone call or lock the device.
- if ([container_ updating])
- [container_ cancelUpdate];
+ if ([tabbar_ updating])
+ [tabbar_ cancelUpdate];
if ([[self superclass] instancesRespondToSelector:@selector(applicationWillResignActive:)])
[super applicationWillResignActive:application];
}
- (void) addStashController {
+ ++locked_;
stash_ = [[CYStashController alloc] init];
[window_ addSubview:[stash_ view]];
}
- (void) removeStashController {
[[stash_ view] removeFromSuperview];
[stash_ release];
+ --locked_;
}
- (void) stash {
- (void) applicationDidFinishLaunching:(id)unused {
_trace();
+ CydiaApp = self;
+
+ [NSURLCache setSharedURLCache:[[[SDURLCache alloc]
+ initWithMemoryCapacity:524288
+ diskCapacity:10485760
+ diskPath:[NSString stringWithFormat:@"%@/Library/Caches/com.saurik.Cydia/SDURLCache", @"/var/root"]
+ ] autorelease]];
+
[CYBrowserController _initialize];
[NSURLProtocol registerClass:[CydiaURLProtocol class]];
database_ = [Database sharedInstance];
[self setupTabBarController];
-
- container_ = [[CYContainer alloc] initWithDatabase:database_];
- [container_ setUpdateDelegate:self];
- [container_ setTabBarController:tabbar_];
- [window_ addSubview:[container_ view]];
+ [tabbar_ setUpdateDelegate:self];
+ [window_ addSubview:[tabbar_ view]];
// Show pinstripes while loading data.
- [[container_ view] setBackgroundColor:[UIColor pinStripeColor]];
+ [[tabbar_ view] setBackgroundColor:[UIColor pinStripeColor]];
[self performSelector:@selector(loadData) withObject:nil afterDelay:0];
_trace();
[container setFrame:containrect];
[spinner setFrame:spinrect];
[label setFrame:textrect];
- [[container_ view] addSubview:container];
+ [[tabbar_ view] addSubview:container];
[self reloadData];
PrintTimes();
[window_ setUserInteractionEnabled:YES];
// XXX: does this actually slow anything down?
- [[container_ view] setBackgroundColor:[UIColor clearColor]];
+ [[tabbar_ view] setBackgroundColor:[UIColor clearColor]];
[container removeFromSuperview];
}