]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
Support NULL Cytore return in PackageFind().
[cydia.git] / MobileCydia.mm
index 8b47cc22ab6a7fff23a5a086f1a44efe5bc2ce7b..fe48f96980f2b6569bcf69bf9a7ac83c31eba10b 100644 (file)
@@ -1,5 +1,5 @@
 /* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008-2010  Jay Freeman (saurik)
+ * Copyright (C) 2008-2011  Jay Freeman (saurik)
 */
 
 /* Modified BSD License {{{ */
@@ -217,14 +217,6 @@ union SplitHash {
 
 static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
 
-void NSLogPoint(const char *fix, const CGPoint &point) {
-    NSLog(@"%s(%g,%g)", fix, point.x, point.y);
-}
-
-void NSLogRect(const char *fix, const CGRect &rect) {
-    NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
-}
-
 static _finline NSString *CydiaURL(NSString *path) {
     char page[25];
     page[0] = 'h'; page[1] = 't'; page[2] = 't'; page[3] = 'p'; page[4] = ':';
@@ -309,15 +301,15 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) {
 @end
 /* }}} */
 
-/* Cydia Action Sheet {{{ */
-@interface CYActionSheet : UIAlertView {
+/* Cydia Alert View {{{ */
+@interface CYAlertView : UIAlertView {
     unsigned button_;
 }
 
 - (int) yieldToPopupAlertAnimated:(BOOL)animated;
 @end
 
-@implementation CYActionSheet
+@implementation CYAlertView
 
 - (id) initWithTitle:(NSString *)title buttons:(NSArray *)buttons defaultButtonIndex:(int)index {
     if ((self = [super init])) {
@@ -363,32 +355,6 @@ static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSC
 static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch;
 static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
 
-/* Information Dictionaries {{{ */
-@interface NSMutableArray (Cydia)
-- (void) addInfoDictionary:(NSDictionary *)info;
-@end
-
-@implementation NSMutableArray (Cydia)
-
-- (void) addInfoDictionary:(NSDictionary *)info {
-    [self addObject:info];
-}
-
-@end
-
-@interface NSMutableDictionary (Cydia)
-- (void) addInfoDictionary:(NSDictionary *)info;
-@end
-
-@implementation NSMutableDictionary (Cydia)
-
-- (void) addInfoDictionary:(NSDictionary *)info {
-    [self setObject:info forKey:[info objectForKey:@"CFBundleIdentifier"]];
-}
-
-@end
-/* }}} */
-
 #define lprintf(args...) fprintf(stderr, args)
 
 #define ForRelease 1
@@ -402,6 +368,7 @@ static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive |
 #define ShowInternals (0 && !ForRelease)
 #define IgnoreInstall (0 && !ForRelease)
 #define AlwaysReload (0 && !ForRelease)
+#define TryIndexedCollation (0 && !ForRelease)
 
 #if !TraceLogging
 #undef _trace
@@ -1071,7 +1038,7 @@ static _transient NSMutableDictionary *Sources_;
 static bool Changed_;
 static time_t now_;
 
-static bool IsWildcat_;
+bool IsWildcat_;
 /* }}} */
 
 /* Display Helpers {{{ */
@@ -1184,7 +1151,6 @@ bool isSectionVisible(NSString *section) {
 @protocol CydiaDelegate
 - (void) retainNetworkActivityIndicator;
 - (void) releaseNetworkActivityIndicator;
-- (void) setPackageController:(CYPackageController *)view;
 - (void) clearPackage:(Package *)package;
 - (void) installPackage:(Package *)package;
 - (void) installPackages:(NSArray *)packages;
@@ -1461,7 +1427,7 @@ struct MetaValue :
 static Cytore::File<MetaValue> MetaFile_;
 // }}}
 // Cytore Helper Functions {{{
-static PackageValue *PackageFind(const char *name, size_t length) {
+static PackageValue *PackageFind(const char *name, size_t length, bool *fail = NULL) {
     SplitHash nhash = { hashlittle(name, length) };
 
     PackageValue *metadata;
@@ -1471,6 +1437,14 @@ static PackageValue *PackageFind(const char *name, size_t length) {
         *offset = MetaFile_.New<PackageValue>(length + 1);
         metadata = &MetaFile_.Get(*offset);
 
+        if (metadata == NULL) {
+            if (fail != NULL)
+                *fail = true;
+
+            metadata = new PackageValue();
+            memset(metadata, 0, sizeof(*metadata));
+        }
+
         memcpy(metadata->name_, name, length + 1);
         metadata->nhash_ = nhash.u16[1];
     } else {
@@ -1486,13 +1460,15 @@ static PackageValue *PackageFind(const char *name, size_t length) {
 }
 
 static void PackageImport(const void *key, const void *value, void *context) {
+    bool &fail(*reinterpret_cast<bool *>(context));
+
     char buffer[1024];
     if (!CFStringGetCString((CFStringRef) key, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
         NSLog(@"failed to import package %@", key);
         return;
     }
 
-    PackageValue *metadata(PackageFind(buffer, strlen(buffer)));
+    PackageValue *metadata(PackageFind(buffer, strlen(buffer), &fail));
     NSDictionary *package((NSDictionary *) value);
 
     if (NSNumber *subscribed = [package objectForKey:@"IsSubscribed"])
@@ -2735,6 +2711,8 @@ struct PackageNameOrdering :
     if (range.location != NSNotFound)
         return YES;
 
+    [self parse];
+
     range = [[self shortDescription] rangeOfString:text options:MatchCompareOptions_];
     if (range.location != NSNotFound)
         return YES;
@@ -4104,18 +4082,16 @@ static NSString *Warning_;
 
     WebDataSource *source([frame dataSource]);
     NSURLResponse *response([source response]);
+
     NSURL *url([response URL]);
     NSString *scheme([url scheme]);
-
-    NSHTTPURLResponse *http;
-    if (scheme != nil && ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]))
-        http = (NSHTTPURLResponse *) response;
-    else
-        http = nil;
-
-    NSDictionary *headers([http allHeaderFields]);
     NSString *host([url host]);
-    [self setHeaders:headers forHost:host];
+
+    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
+        NSHTTPURLResponse *http((NSHTTPURLResponse *) response);
+        NSDictionary *headers([http allHeaderFields]);
+        [self setHeaders:headers forHost:host];
+    }
 
     if (
         [host isEqualToString:@"cydia.saurik.com"] ||
@@ -4349,7 +4325,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
             initWithTitle:UCLocalize("CANCEL")
-            // OLD: [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("CANCEL"), UCLocalize("QUEUE")]
             style:UIBarButtonItemStylePlain
             target:self
             action:@selector(cancelButtonClicked)
@@ -4362,7 +4337,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (issues_ == nil && ![self isLoading])
         [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
             initWithTitle:UCLocalize("CONFIRM")
-            style:UIBarButtonItemStylePlain
+            style:UIBarButtonItemStyleDone
             target:self
             action:@selector(confirmButtonClicked)
         ] autorelease]];
@@ -4498,7 +4473,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         //[status_ setFont:font];
 
         output_ = [[UITextView alloc] init];
-
         [output_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
         //[output_ setTextFont:@"Courier New"];
         [output_ setFont:[[output_ font] fontWithSize:12]];
@@ -4544,7 +4518,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         10,
         20,
         bounds.size.width - 20,
-        bounds.size.height - 62
+        bounds.size.height - 96
     )];
     [close_ setFrame:CGRectMake(
         (bounds.size.width - closewidth) / 2,
@@ -4754,7 +4728,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
-    CYActionSheet *sheet([[[CYActionSheet alloc]
+    CYAlertView *sheet([[[CYAlertView alloc]
         initWithTitle:title
         buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil]
         defaultButtonIndex:0
@@ -5010,6 +4984,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self setNeedsDisplay];
 }
 
+- (NSString *) accessibilityLabel {
+    return [NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), name_, description_];
+}
+
 - (void) setPackage:(Package *)package {
     [self clearPackage];
     [package parse];
@@ -5238,6 +5216,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)];
 }
 
+- (NSString *) accessibilityLabel {
+    return name_;
+}
+
 - (void) drawContentRect:(CGRect)rect {
     bool highlighted(highlighted_ && !editing_);
 
@@ -5434,8 +5416,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) release {
-    if ([self retainCount] == 1)
-        [delegate_ setPackageController:self];
     [super release];
 }
 
@@ -5536,11 +5516,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 #endif
 
+- (void) viewWillAppear:(BOOL)animated {
+    if (![self hasLoaded])
+        [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/package/")]];
+    [super viewWillAppear:animated];
+}
+
 - (id) initWithDatabase:(Database *)database {
     if ((self = [super init]) != nil) {
         database_ = database;
         buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
-        [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
     } return self;
 }
 
@@ -5602,14 +5587,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) reloadData {
+    [super reloadData];
+
     [self setPackage:[database_ packageWithName:name_]];
 }
 
 @end
 /* }}} */
 
-/* Package Table {{{ */
-@interface PackageTable : UIView <
+/* Package List Controller {{{ */
+@interface PackageListController : CYViewController <
     UITableViewDataSource,
     UITableViewDelegate
 > {
@@ -5620,29 +5607,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     UITableView *list_;
     NSMutableArray *index_;
     NSMutableDictionary *indices_;
-    // XXX: this target_ seems to be delegate_. :(
-    _transient id target_;
-    SEL action_;
-    // XXX: why do we even have this delegate_?
-    _transient id delegate_;
+    NSString *title_;
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action;
-
+- (id) initWithDatabase:(Database *)database title:(NSString *)title;
 - (void) setDelegate:(id)delegate;
-
-- (void) reloadData;
 - (void) resetCursor;
 
-- (UITableView *) list;
-
-- (void) setShouldHideHeaderInShortLists:(BOOL)hide;
-
-- (void) deselectWithAnimation:(BOOL)animated;
-
 @end
 
-@implementation PackageTable
+@implementation PackageListController
 
 - (void) dealloc {
     [packages_ release];
@@ -5650,13 +5624,98 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [list_ release];
     [index_ release];
     [indices_ release];
+    [title_ release];
 
     [super dealloc];
 }
 
+- (void) deselectWithAnimation:(BOOL)animated {
+    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
+}
+
+- (void) resizeForKeyboardBounds:(CGRect)bounds duration:(NSTimeInterval)duration curve:(UIViewAnimationCurve)curve {
+    CGRect base = [[self view] bounds];
+    base.size.height -= bounds.size.height;
+    base.origin = [list_ frame].origin;
+
+    [UIView beginAnimations:nil context:NULL];
+    [UIView setAnimationBeginsFromCurrentState:YES];
+    [UIView setAnimationCurve:curve];
+    [UIView setAnimationDuration:duration];
+    [list_ setFrame:base];
+    [UIView commitAnimations];
+}
+
+- (void) resizeForKeyboardBounds:(CGRect)bounds duration:(NSTimeInterval)duration {
+    [self resizeForKeyboardBounds:bounds duration:duration curve:UIViewAnimationCurveLinear];
+}
+
+- (void) resizeForKeyboardBounds:(CGRect)bounds {
+    [self resizeForKeyboardBounds:bounds duration:0];
+}
+
+- (void) keyboardWillShow:(NSNotification *)notification {
+    CGRect bounds;
+    CGPoint center;
+    NSTimeInterval duration;
+    UIViewAnimationCurve curve;
+    [[[notification userInfo] objectForKey:UIKeyboardBoundsUserInfoKey] getValue:&bounds];
+    [[[notification userInfo] objectForKey:UIKeyboardCenterEndUserInfoKey] getValue:&center];
+    [[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
+    [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
+
+    CGRect kbframe = CGRectMake(round(center.x - bounds.size.width / 2.0), round(center.y - bounds.size.height / 2.0), bounds.size.width, bounds.size.height);
+    UIViewController *base = self;
+    while ([base parentViewController] != nil)
+        base = [base parentViewController];
+    CGRect viewframe = [[base view] convertRect:[list_ frame] fromView:[list_ superview]];
+    CGRect intersection = CGRectIntersection(viewframe, kbframe);
+
+    [self resizeForKeyboardBounds:intersection duration:duration curve:curve];
+}
+
+- (void) keyboardWillHide:(NSNotification *)notification {
+    NSTimeInterval duration;
+    UIViewAnimationCurve curve;
+    [[[notification userInfo] objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve];
+    [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration];
+
+    [self resizeForKeyboardBounds:CGRectZero duration:duration curve:curve];
+}
+
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+
+    [self resizeForKeyboardBounds:CGRectZero];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
+}
+
+- (void) viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+
+    [self resizeForKeyboardBounds:CGRectZero];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
+}
+
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [self deselectWithAnimation:animated];
+}
+
+- (void) didSelectPackage:(Package *)package {
+    CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_] autorelease]);
+    [view setPackage:package];
+    [view setDelegate:delegate_];
+    [[self navigationController] pushViewController:view animated:YES];
+}
+
+#if TryIndexedCollation
 + (BOOL) hasIndexedCollation {
     return NO; // XXX: objc_getClass("UILocalizedIndexedCollation") != nil;
 }
+#endif
 
 - (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
     NSInteger count([sections_ count]);
@@ -5694,15 +5753,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return cell;
 }
 
-- (void) deselectWithAnimation:(BOOL)animated {
-    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
-}
-
-- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
+- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
     Package *package([self packageAtIndexPath:path]);
     package = [database_ packageWithName:[package id]];
-    [target_ performSelector:action_ withObject:package];
-    return path;
+    [self didSelectPackage:package];
 }
 
 - (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView {
@@ -5711,32 +5765,37 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
+#if TryIndexedCollation
     if ([[self class] hasIndexedCollation]) {
         return [[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionForSectionIndexTitleAtIndex:index];
     }
+#endif
 
     return index;
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action {
-    if ((self = [super initWithFrame:frame]) != nil) {
+- (id) initWithDatabase:(Database *)database title:(NSString *)title {
+    if ((self = [super init]) != nil) {
         database_ = database;
+        title_ = [title copy];
+        [[self navigationItem] setTitle:title_];
 
-        target_ = target;
-        action_ = action;
+#if TryIndexedCollation
+        if ([[self class] hasIndexedCollation])
+            index_ = [[[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionIndexTitles] retain]
+        else
+#endif
+            index_ = [[NSMutableArray alloc] initWithCapacity:32];
 
-        index_ = [[self class] hasIndexedCollation]
-            ? [[[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionIndexTitles] retain]
-            : [[NSMutableArray alloc] initWithCapacity:32];
         indices_ = [[NSMutableDictionary alloc] initWithCapacity:32];
 
         packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
-        list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain];
+        list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
         [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
         [list_ setRowHeight:73];
-        [self addSubview:list_];
+        [[self view] addSubview:list_];
 
         [list_ setDataSource:self];
         [list_ setDelegate:self];
@@ -5752,6 +5811,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) reloadData {
+    [super reloadData];
+
     era_ = [database_ era];
     NSArray *packages = [database_ packages];
 
@@ -5768,6 +5829,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     Section *section = nil;
 
+#if TryIndexedCollation
     if ([[self class] hasIndexedCollation]) {
         id collation = [objc_getClass("UILocalizedIndexedCollation") currentCollation];
         NSArray *titles = [collation sectionIndexTitles];
@@ -5798,7 +5860,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 [section addToCount];
             }
         _end
-    } else {
+    } else
+#endif
+    {
         [index_ removeAllObjects];
 
         _profile(PackageTable$reloadData$Section)
@@ -5838,18 +5902,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [list_ scrollRectToVisible:CGRectMake(0, 0, 0, 0) animated:NO];
 }
 
-- (UITableView *) list {
-    return list_;
-}
-
-- (void) setShouldHideHeaderInShortLists:(BOOL)hide {
-    //XXX:[list_ setShouldHideHeaderInShortLists:hide];
-}
-
 @end
 /* }}} */
-/* Filtered Package Table {{{ */
-@interface FilteredPackageTable : PackageTable {
+/* Filtered Package List Controller {{{ */
+@interface FilteredPackageListController : PackageListController {
     SEL filter_;
     IMP imp_;
     id object_;
@@ -5858,11 +5914,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) setObject:(id)object;
 - (void) setObject:(id)object forFilter:(SEL)filter;
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action filter:(SEL)filter with:(id)object;
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
 
 @end
 
-@implementation FilteredPackageTable
+@implementation FilteredPackageListController
 
 - (void) dealloc {
     if (object_ != nil)
@@ -5900,94 +5956,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _end
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action filter:(SEL)filter with:(id)object {
-    if ((self = [super initWithFrame:frame database:database target:target action:action]) != nil) {
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
+    if ((self = [super initWithDatabase:database title:title]) != nil) {
         [self setFilter:filter];
-        object_ = [object retain];
+        [self setObject:object];
         [self reloadData];
     } return self;
 }
 
 @end
-/* }}} */
-/* Filtered Package Controller {{{ */
-@interface FilteredPackageController : CYViewController {
-    _transient Database *database_;
-    FilteredPackageTable *packages_;
-    NSString *title_;
-    SEL filter_;
-    id object_;
-}
-
-- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
-
-@end
-
-@implementation FilteredPackageController
-
-- (void) dealloc {
-    [self releaseSubviews];
-    [title_ release];
-
-    [super dealloc];
-}
-
-- (void) viewDidAppear:(BOOL)animated {
-    [super viewDidAppear:animated];
-    [packages_ deselectWithAnimation:animated];
-}
-
-- (void) didSelectPackage:(Package *)package {
-    CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_] autorelease]);
-    [view setPackage:package];
-    [view setDelegate:delegate_];
-    [[self navigationController] pushViewController:view animated:YES];
-}
-
-- (void) loadView {
-    [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]];
-
-    packages_ = [[FilteredPackageTable alloc]
-        initWithFrame:[[self view] bounds]
-        database:database_
-        target:self
-        action:@selector(didSelectPackage:)
-        filter:filter_
-        with:object_
-    ];
-    [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
-    [[self view] addSubview:packages_];
-}
-
-- (void) viewDidLoad {
-    [[self navigationItem] setTitle:title_];
-}
-
-- (void) releaseSubviews {
-    [packages_ release];
-}
-
-- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
-    if ((self = [super init]) != nil) {
-        database_ = database;
-        title_ = [title copy];
-        filter_ = filter;
-        object_ = object;
-    } return self;
-}
-
-- (void) reloadData {
-    [super reloadData];
-    [packages_ reloadData];
-}
-
-- (void) setDelegate:(id)delegate {
-    [super setDelegate:delegate];
-    [packages_ setDelegate:delegate];
-}
-
-@end
-
 /* }}} */
 
 /* Home Controller {{{ */
@@ -5997,7 +5974,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @implementation HomeController
 
-+ (BOOL)shouldHideNavigationBar {
++ (BOOL) shouldHideNavigationBar {
     return NO;
 }
 
@@ -6024,7 +6001,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [alert setCancelButtonIndex:0];
 
     [alert setMessage:
-        @"Copyright (C) 2008-2010\n"
+        @"Copyright (C) 2008-2011\n"
         "Jay Freeman (saurik)\n"
         "saurik@saurik.com\n"
         "http://www.saurik.com/"
@@ -6033,13 +6010,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [alert show];
 }
 
-- (void) viewWillAppear:(BOOL)animated {
-    [super viewWillAppear:animated];
-
-    if ([[self class] shouldHideNavigationBar])
-        [[self navigationController] setNavigationBarHidden:YES animated:animated];
-}
-
 - (void) viewWillDisappear:(BOOL)animated {
     [super viewWillDisappear:animated];
 
@@ -6047,17 +6017,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [[self navigationController] setNavigationBarHidden:NO animated:animated];
 }
 
-- (id) init {
-    if ((self = [super init]) != nil) {
-        [self loadURL:[NSURL URLWithString:CydiaURL(@"")]];
+- (void) viewWillAppear:(BOOL)animated {
+    if (![self hasLoaded])
+        [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/home/")]];
 
-        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
-            initWithTitle:UCLocalize("ABOUT")
-            style:UIBarButtonItemStylePlain
-            target:self
-            action:@selector(aboutButtonClicked)
-        ] autorelease]];
-    } return self;
+    [super viewWillAppear:animated];
+
+    if ([[self class] shouldHideNavigationBar])
+        [[self navigationController] setNavigationBarHidden:YES animated:animated];
+}
+
+- (void) viewDidLoad {
+    [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+        initWithTitle:UCLocalize("ABOUT")
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(aboutButtonClicked)
+    ] autorelease]];
 }
 
 @end
@@ -6075,21 +6051,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [NSURL URLWithString:@"cydia://manage"];
 }
 
-- (id) init {
-    if ((self = [super init]) != nil) {
-        [[self navigationItem] setTitle:UCLocalize("MANAGE")];
+- (void) viewWillAppear:(BOOL)animated {
+    if (![self hasLoaded])
+        [self loadURL:[NSURL URLWithString:CydiaURL(@"ui/manage/")]];
 
-        [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]];
+    [super viewWillAppear:animated];
+}
 
-        [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
-            initWithTitle:UCLocalize("SETTINGS")
-            style:UIBarButtonItemStylePlain
-            target:self
-            action:@selector(settingsButtonClicked)
-        ] autorelease]];
+- (void) viewDidLoad {
+    [[self navigationItem] setTitle:UCLocalize("MANAGE")];
 
-        [self queueStatusDidChange];
-    } return self;
+    [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+        initWithTitle:UCLocalize("SETTINGS")
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(settingsButtonClicked)
+    ] autorelease]];
+
+    [self queueStatusDidChange];
 }
 
 - (void) settingsButtonClicked {
@@ -6102,11 +6081,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) applyLoadingTitle {
-    // No "Loading" title.
+    // Disable "Loading" title.
 }
 
 - (void) applyRightButton {
-    // No right button.
+    // Disable right button.
 }
 #endif
 
@@ -6126,6 +6105,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (bool) isLoading {
+    // Never show as loading.
     return false;
 }
 
@@ -6265,7 +6245,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     id root_;
 }
 
-- (NSArray *) navigationURLItems;
+- (NSArray *) navigationURLCollection;
 - (void) dropBar:(BOOL)animated;
 - (void) beginUpdate;
 - (void) raiseBar:(BOOL)animated;
@@ -6275,12 +6255,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @implementation CYTabBarController
 
-- (NSArray *) navigationURLItems {
+- (NSArray *) navigationURLCollection {
     NSMutableArray *items([NSMutableArray array]);
 
-    // XXX:Deal with transient view controllers.
+    // XXX: Should this deal with transient view controllers?
     for (id navigation in [self viewControllers]) {
-        NSArray *stack = [navigation performSelector:@selector(navigationURLStack)];
+        NSArray *stack = [navigation performSelector:@selector(navigationURLCollection)];
         if (stack != nil)
             [items addObject:stack];
     }
@@ -6289,15 +6269,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) reloadData {
-    size_t count([[self viewControllers] count]);
-    for (size_t i(0); i != count; ++i) {
-        CYNavigationController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
-        [page reloadData];
-    }
+    for (CYViewController *controller in [self viewControllers])
+        [controller reloadData];
 
     [(CYNavigationController *)[self transientViewController] reloadData];
 }
 
+- (void) dealloc {
+    [refreshbar_ release];
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+    [super dealloc];
+}
+
 - (id) initWithDatabase:(Database *)database {
     if ((self = [super init]) != nil) {
         database_ = database;
@@ -6522,12 +6506,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-- (void) dealloc {
-    [refreshbar_ release];
-    [[NSNotificationCenter defaultCenter] removeObserver:self];
-    [super dealloc];
-}
-
 @end
 /* }}} */
 /* Cydia Navigation Controller {{{ */
@@ -6536,7 +6514,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _transient id<UINavigationControllerDelegate> delegate_;
 }
 
-- (NSArray *) navigationURLStack;
+- (NSArray *) navigationURLCollection;
 - (id) initWithDatabase:(Database *)database;
 - (void) reloadData;
 
@@ -6549,7 +6527,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (NSArray *) navigationURLStack {
+- (NSArray *) navigationURLCollection {
     NSMutableArray *stack([NSMutableArray array]);
 
     for (CYViewController *controller in [self viewControllers]) {
@@ -6562,10 +6540,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (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];
+    for (CYViewController *page in [self viewControllers]) {
+        if ([page hasLoaded])
+            [page reloadData];
     }
 }
 
@@ -6684,7 +6661,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 /* }}} */
 
 /* Section Controller {{{ */
-@interface SectionController : FilteredPackageController {
+@interface SectionController : FilteredPackageListController {
     NSString *section_;
 }
 
@@ -6765,6 +6742,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
+- (BOOL) isEditing {
+    return editing_;
+}
+
 - (void) setEditing:(BOOL)editing {
     if ((editing_ = editing))
         [list_ reloadData];
@@ -6800,7 +6781,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     static NSString *reuseIdentifier = @"SectionCell";
 
-    SectionCell *cell = (SectionCell *) [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+    SectionCell *cell = (SectionCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
     if (cell == nil)
         cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
 
@@ -6949,6 +6930,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return [NSURL URLWithString:@"cydia://changes"];
 }
 
+- (void) viewWillAppear:(BOOL)animated {
+    // Loads after it appears, so don't load beforehand.
+    loaded_ = YES;
+    [super viewWillAppear:animated];
+}
+
 - (void) viewDidAppear:(BOOL)animated {
     [super viewDidAppear:animated];
 
@@ -7044,9 +7031,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if ((self = [super init]) != nil) {
         database_ = database;
 
-        // We load after the view is visible, so don't "magically" load beforehand.
-        loaded_ = YES;
-
         packages_ = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
     } return self;
@@ -7170,7 +7154,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Search Controller {{{ */
-@interface SearchController : FilteredPackageController <
+@interface SearchController : FilteredPackageListController <
     UISearchBarDelegate
 > {
     UISearchBar *search_;
@@ -7191,7 +7175,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (NSURL *) navigationURL {
-    return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://search/%@", [search_ text]]];
+    if ([search_ text] == nil || [[search_ text] isEqualToString:@""])
+        return [NSURL URLWithString:@"cydia://search"];
+    else
+        return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://search/%@", [search_ text]]];
 }
 
 - (void) setSearchTerm:(NSString *)searchTerm {
@@ -7200,13 +7187,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
-    [packages_ setObject:[search_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)];
+    [self setObject:[search_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)];
     [search_ resignFirstResponder];
     [self reloadData];
 }
 
 - (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)text {
-    [packages_ setObject:[search_ text] forFilter:@selector(isUnfilteredAndSelectedForBy:)];
+    [self setObject:text forFilter:@selector(isUnfilteredAndSelectedForBy:)];
     [self reloadData];
 }
 
@@ -7239,9 +7226,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) reloadData {
-    [packages_ setObject:[search_ text]];
+    [self setObject:[search_ text]];
     [super reloadData];
-    [packages_ resetCursor];
+    [self resetCursor];
 }
 
 - (void) didSelectPackage:(Package *)package {
@@ -7407,51 +7394,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [table_ reloadData];
 }
 
-@end
-/* }}} */
-/* Signature Controller {{{ */
-@interface SignatureController : CYBrowserController {
-    _transient Database *database_;
-    NSString *package_;
-}
-
-- (id) initWithDatabase:(Database *)database package:(NSString *)package;
-
-@end
-
-@implementation SignatureController
-
-- (void) dealloc {
-    [package_ release];
-    [super dealloc];
-}
-
-- (NSURL *) navigationURL {
-    return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/signature", package_]];
-}
-
-- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    // XXX: dude!
-    [super webView:view didClearWindowObject:window forFrame:frame];
-}
-
-- (id) initWithDatabase:(Database *)database package:(NSString *)package {
-    if ((self = [super init]) != nil) {
-        database_ = database;
-        package_ = [package retain];
-        [self reloadData];
-    } return self;
-}
-
-- (void) reloadData {
-    [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
-}
-
 @end
 /* }}} */
 
 /* Installed Controller {{{ */
-@interface InstalledController : FilteredPackageController {
+@interface InstalledController : FilteredPackageListController {
     BOOL expert_;
 }
 
@@ -7502,10 +7449,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 #endif
 }
 
-- (void) reloadData {
-    [packages_ reloadData];
-}
-
 - (void) updateRoleButton {
     if (Role_ != nil && ![Role_ isEqualToString:@"Developer"])
         [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
@@ -7517,18 +7460,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) roleButtonClicked {
-    [packages_ setObject:[NSNumber numberWithBool:expert_]];
-    [packages_ reloadData];
+    [self setObject:[NSNumber numberWithBool:expert_]];
+    [self reloadData];
     expert_ = !expert_;
 
     [self updateRoleButton];
 }
 
-- (void) setDelegate:(id)delegate {
-    [super setDelegate:delegate];
-    [packages_ setDelegate:delegate];
-}
-
 @end
 /* }}} */
 
@@ -7592,6 +7530,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     } return self;
 }
 
+- (NSString *) accessibilityLabel {
+    return label_;
+}
+
 - (void) drawContentRect:(CGRect)rect {
     bool highlighted(highlighted_);
     float width(rect.size.width);
@@ -7614,7 +7556,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Source Controller {{{ */
-@interface SourceController : FilteredPackageController {
+@interface SourceController : FilteredPackageListController {
     Source *source_;
 }
 
@@ -8497,8 +8439,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (recently || loaded_ || ManualRefresh) {
         [self performSelectorOnMainThread:@selector(_loaded) withObject:nil waitUntilDone:NO];
 
-        // If we are cancelling due to ManualRefresh or a recent refresh
-        // we need to make sure it knows it's already loaded.
+        // If we are cancelling, we need to make sure it knows it's already loaded.
         loaded_ = true;
         return;
     } else {
@@ -8751,12 +8692,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [self setNetworkActivityIndicatorVisible:NO];
 }
 
-- (void) setPackageController:(CYPackageController *)view {
-    WebThreadLock();
-    [view setPackage:nil];
-    WebThreadUnlock();
-}
-
 - (void) cancelAndClear:(bool)clear {
     @synchronized (self) {
         if (clear) {
@@ -8989,8 +8924,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         if ([base isEqualToString:@"package"]) {
             if ([arg2 isEqualToString:@"settings"]) {
                 controller = [[[PackageSettingsController alloc] initWithDatabase:database_ package:arg1] autorelease];
-            } else if ([arg2 isEqualToString:@"signature"]) {
-                controller = [[[SignatureController alloc] initWithDatabase:database_ package:arg1] autorelease];
             } else if ([arg2 isEqualToString:@"files"]) {
                 if (Package *package = [database_ packageWithName:arg1]) {
                     controller = [[[FileTable alloc] initWithDatabase:database_] autorelease];
@@ -9033,7 +8966,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) applicationWillTerminate:(UIApplication *)application {
-    [Metadata_ setObject:[tabbar_ navigationURLItems] forKey:@"InterfaceState"];
+    Changed_ = true;
+    [Metadata_ setObject:[tabbar_ navigationURLCollection] forKey:@"InterfaceState"];
     [Metadata_ setObject:[NSDate date] forKey:@"LastClosed"];
     [Metadata_ setObject:[NSNumber numberWithInt:[tabbar_ selectedIndex]] forKey:@"InterfaceIndex"];
 
@@ -9305,6 +9239,33 @@ MSHook(void, UIHardware$_playSystemSound$, Class self, SEL _cmd, int sound) {
     }
 }
 
+Class $UIApplication;
+
+MSHook(void, UIApplication$_updateApplicationAccessibility, UIApplication *self, SEL _cmd) {
+    static BOOL initialized = NO;
+    static BOOL started = NO;
+
+    NSDictionary *dict([[[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.Accessibility.plist"] autorelease]);
+    BOOL enabled = [[dict objectForKey:@"VoiceOverTouchEnabled"] boolValue] || [[dict objectForKey:@"VoiceOverTouchEnabledByiTunes"] boolValue];
+
+    if ([self respondsToSelector:@selector(_accessibilityBundlePrincipalClass)]) {
+        id bundle = [self performSelector:@selector(_accessibilityBundlePrincipalClass)];
+        if (![bundle respondsToSelector:@selector(_accessibilityStopServer)]) return;
+        if (![bundle respondsToSelector:@selector(_accessibilityStartServer)]) return;
+
+        if (initialized && !enabled) {
+            initialized = NO;
+            [bundle performSelector:@selector(_accessibilityStopServer)];
+        } else if (enabled) {
+            initialized = YES;
+            if (!started) {
+                started = YES;
+                [bundle performSelector:@selector(_accessibilityStartServer)];
+            }
+        }
+    }
+}
+
 int main(int argc, char *argv[]) { _pooled
     _trace();
 
@@ -9332,6 +9293,13 @@ int main(int argc, char *argv[]) { _pooled
         _UIHardware$_playSystemSound$ = reinterpret_cast<void (*)(Class, SEL, int)>(method_getImplementation(UIHardware$_playSystemSound$));
         method_setImplementation(UIHardware$_playSystemSound$, reinterpret_cast<IMP>(&$UIHardware$_playSystemSound$));
     }
+
+    $UIApplication = objc_getClass("UIApplication");
+    Method UIApplication$_updateApplicationAccessibility(class_getInstanceMethod($UIApplication, @selector(_updateApplicationAccessibility)));
+    if (UIApplication$_updateApplicationAccessibility != NULL) {
+        _UIApplication$_updateApplicationAccessibility = reinterpret_cast<void (*)(UIApplication *, SEL)>(method_getImplementation(UIApplication$_updateApplicationAccessibility));
+        method_setImplementation(UIApplication$_updateApplicationAccessibility, reinterpret_cast<IMP>(&$UIApplication$_updateApplicationAccessibility));
+    }
     /* }}} */
     /* Set Locale {{{ */
     Locale_ = CFLocaleCopyCurrent();
@@ -9505,11 +9473,15 @@ int main(int argc, char *argv[]) { _pooled
     _trace();
 
     if (Packages_ != nil) {
-        CFDictionaryApplyFunction((CFDictionaryRef) Packages_, &PackageImport, NULL);
+        bool fail(false);
+        CFDictionaryApplyFunction((CFDictionaryRef) Packages_, &PackageImport, &fail);
         _trace();
-        [Metadata_ removeObjectForKey:@"Packages"];
-        Packages_ = nil;
-        Changed_ = true;
+
+        if (!fail) {
+            [Metadata_ removeObjectForKey:@"Packages"];
+            Packages_ = nil;
+            Changed_ = true;
+        }
     }
 
     Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];