+ [overlay_ setBarStyle:1];
+ [underlay_ setBarStyle:1];
+
+ int barstyle = [overlay_ _barStyle:NO];
+ bool ugly = barstyle == 0;
+
+ UIProgressIndicatorStyle style = ugly ?
+ UIProgressIndicatorStyleMediumBrown :
+ UIProgressIndicatorStyleMediumWhite;
+
+ CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
+ unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
+ CGRect indrect = {{indoffset, indoffset}, indsize};
+
+ indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
+ [indicator_ setStyle:style];
+ [overlay_ addSubview:indicator_];
+
+ CGSize prmsize = {215, indsize.height + 4};
+
+ CGRect prmrect = {{
+ indoffset * 2 + indsize.width,
+#ifdef __OBJC2__
+ -1 +
+#endif
+ unsigned(ovrrect.size.height - prmsize.height) / 2
+ }, prmsize};
+
+ UIFont *font = [UIFont systemFontOfSize:15];
+
+ prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
+
+ [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
+ [prompt_ setBackgroundColor:[UIColor clearColor]];
+ [prompt_ setFont:font];
+
+ [overlay_ addSubview:prompt_];
+
+ CGSize prgsize = {75, 100};
+
+ CGRect prgrect = {{
+ ovrrect.size.width - prgsize.width - 10,
+ (ovrrect.size.height - prgsize.height) / 2
+ } , prgsize};
+
+ progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
+ [progress_ setStyle:0];
+ [overlay_ addSubview:progress_];
+
+ cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
+ [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
+
+ CGRect frame = [cancel_ frame];
+ frame.size.width = 65;
+ frame.origin.x = ovrrect.size.width - frame.size.width - 5;
+ frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
+ [cancel_ setFrame:frame];
+
+ [cancel_ setBarStyle:barstyle];
+ } return self;
+}
+
+- (void) _onCancel {
+ updating_ = false;
+ [cancel_ removeFromSuperview];
+}
+
+- (void) _update { _pooled
+ Status status;
+ status.setDelegate(self);
+
+ [database_ updateWithStatus:status];
+
+ [self
+ performSelectorOnMainThread:@selector(_update_)
+ withObject:nil
+ waitUntilDone:NO
+ ];
+}
+
+- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
+ [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
+}
+
+- (void) setProgressTitle:(NSString *)title {
+ [self
+ performSelectorOnMainThread:@selector(_setProgressTitle:)
+ withObject:title
+ waitUntilDone:YES
+ ];
+}
+
+- (void) setProgressPercent:(float)percent {
+ [self
+ performSelectorOnMainThread:@selector(_setProgressPercent:)
+ withObject:[NSNumber numberWithFloat:percent]
+ waitUntilDone:YES
+ ];
+}
+
+- (void) startProgress {
+}
+
+- (void) addProgressOutput:(NSString *)output {
+ [self
+ performSelectorOnMainThread:@selector(_addProgressOutput:)
+ withObject:output
+ waitUntilDone:YES
+ ];
+}
+
+- (bool) isCancelling:(size_t)received {
+ NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
+ if (received_ != received) {
+ received_ = received;
+ last_ = now;
+ } else if (now - last_ > 15)
+ return true;
+ return !updating_;
+}
+
+- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+ [sheet dismiss];
+}
+
+- (void) _setProgressTitle:(NSString *)title {
+ [prompt_ setText:title];
+}
+
+- (void) _setProgressPercent:(NSNumber *)percent {
+ [progress_ setProgress:[percent floatValue]];
+}
+
+- (void) _addProgressOutput:(NSString *)output {
+}
+
+@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) 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;
+ Package *package([database packageWithName:path]);
+ if (package == nil)
+ goto fail;
+
+ NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
+
+ UIImage *icon([package icon]);
+ NSData *data(UIImagePNGRepresentation(icon));
+
+ [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [client URLProtocol:self didLoadData:data];
+ [client URLProtocolDidFinishLoading:self];
+ } else fail: {
+ [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
+ }
+}
+
+- (void) stopLoading {
+}
+
+@end
+/* }}} */
+
+/* Install View {{{ */
+@interface InstallView : RVPage {
+ _transient Database *database_;
+ NSMutableArray *sections_;
+ NSMutableArray *filtered_;
+ UITransitionView *transition_;
+ UITable *list_;
+ UIView *accessory_;
+ BOOL editing_;
+}
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (void) reloadData;
+- (void) resetView;
+
+@end
+
+@implementation InstallView
+
+- (void) dealloc {
+ [list_ setDataSource:nil];
+ [list_ setDelegate:nil];
+
+ [sections_ release];
+ [filtered_ release];
+ [transition_ release];
+ [list_ release];
+ [accessory_ release];
+ [super dealloc];
+}
+
+- (int) numberOfRowsInTable:(UITable *)table {
+ return editing_ ? [sections_ count] : [filtered_ count] + 1;
+}
+
+- (float) table:(UITable *)table heightForRow:(int)row {
+ return 45;
+}
+
+- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
+ if (reusing == nil)
+ reusing = [[[SectionCell alloc] init] autorelease];
+ [(SectionCell *)reusing setSection:(editing_ ?
+ [sections_ objectAtIndex:row] :
+ (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
+ ) editing:editing_];
+ return reusing;
+}
+
+- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
+ return !editing_;
+}
+
+- (BOOL) table:(UITable *)table canSelectRow:(int)row {
+ return !editing_;
+}
+
+- (void) tableRowSelected:(NSNotification *)notification {
+ int row = [[notification object] selectedRow];
+ if (row == INT_MAX)
+ return;
+
+ Section *section;
+ NSString *name;
+ NSString *title;
+
+ if (row == 0) {
+ section = nil;
+ name = nil;
+ title = @"All Packages";
+ } else {
+ section = [filtered_ objectAtIndex:(row - 1)];
+ name = [section name];
+
+ if (name != nil)
+ title = name;
+ else {
+ name = @"";
+ title = @"(No Section)";
+ }
+ }
+
+ PackageTable *table = [[[PackageTable alloc]
+ initWithBook:book_
+ database:database_
+ title:title
+ filter:@selector(isVisiblyUninstalledInSection:)
+ with:name
+ ] autorelease];
+
+ [table setDelegate:delegate_];
+
+ [book_ pushPage:table];
+}
+
+- (id) initWithBook:(RVBook *)book database:(Database *)database {
+ if ((self = [super initWithBook:book]) != nil) {
+ database_ = database;
+
+ sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
+ filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
+
+ transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
+ [self addSubview:transition_];
+
+ list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
+ [transition_ transition:0 toView:list_];
+
+ UITableColumn *column = [[[UITableColumn alloc]
+ initWithTitle:@"Name"
+ identifier:@"name"
+ width:[self frame].size.width
+ ] autorelease];
+
+ [list_ setDataSource:self];
+ [list_ setSeparatorStyle:1];
+ [list_ addTableColumn:column];
+ [list_ setDelegate:self];
+ [list_ setReusesTableCells:YES];
+
+ [self reloadData];
+
+ [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+ [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+ } return self;
+}
+
+- (void) reloadData {
+ NSArray *packages = [database_ packages];
+
+ [sections_ removeAllObjects];
+ [filtered_ removeAllObjects];
+
+ NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
+ NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
+
+ _trace();
+ for (size_t i(0); i != [packages count]; ++i) {
+ Package *package([packages objectAtIndex:i]);
+ NSString *name([package section]);
+
+ if (name != nil) {
+ Section *section([sections objectForKey:name]);
+ if (section == nil) {
+ section = [[[Section alloc] initWithName:name] autorelease];
+ [sections setObject:section forKey:name];
+ }
+ }