]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Removed reloading data when clearing queue. Method of clearing without reloading...
[cydia.git] / Cydia.mm
index e0bd10a3cd3ebe18b72d9ab9371cbc03a2bab485..b7d2b35157d42a97fd8579587711b358cd8387f6 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -1,5 +1,5 @@
 /* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008-2009  Jay Freeman (saurik)
+ * Copyright (C) 2008-2010  Jay Freeman (saurik)
 */
 
 /* Modified BSD License {{{ */
 #define USE_SYSTEM_MALLOC 1
 
 /* #include Directives {{{ */
-#import "UICaboodle/UCPlatform.h"
-#import "UICaboodle/UCLocalize.h"
+#include "UICaboodle/UCPlatform.h"
+#include "UICaboodle/UCLocalize.h"
 
 #include <objc/objc.h>
 #include <objc/runtime.h>
 
 #include <CoreGraphics/CoreGraphics.h>
-#include <GraphicsServices/GraphicsServices.h>
 #include <Foundation/Foundation.h>
 
 #if 0
 #include <CoreFoundation/CFPriv.h>
 #include <CoreFoundation/CFUniChar.h>
 
-#import <UIKit/UIKit.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+#include <UIKit/UIKit.h>
+#include "iPhonePrivate.h"
+
+#include <IOKit/IOKitLib.h>
 
 #include <WebCore/WebCoreThread.h>
-#import <WebKit/WebDefaultUIKitDelegate.h>
 
 #include <algorithm>
 #include <iomanip>
@@ -72,6 +75,8 @@
 
 #include <ext/stdio_filebuf.h>
 
+#undef ABS
+
 #include <apt-pkg/acquire.h>
 #include <apt-pkg/acquire-item.h>
 #include <apt-pkg/algorithms.h>
 #include <sys/param.h>
 #include <sys/mount.h>
 
+#include <fcntl.h>
 #include <notify.h>
 #include <dlfcn.h>
 
@@ -114,10 +120,9 @@ extern "C" {
 
 #include <ext/hash_map>
 
-#import "UICaboodle/BrowserView.h"
-#import "UICaboodle/ResetView.h"
+#include "UICaboodle/BrowserView.h"
 
-#import "substrate.h"
+#include "substrate.h"
 /* }}} */
 
 /* Profiler {{{ */
@@ -192,6 +197,8 @@ void PrintTimes() {
 
 #define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
 
+static const NSUInteger UIViewAutoresizingFlexibleBoth(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+
 void NSLogPoint(const char *fix, const CGPoint &point) {
     NSLog(@"%s(%g,%g)", fix, point.x, point.y);
 }
@@ -210,6 +217,15 @@ static _finline NSString *CydiaURL(NSString *path) {
     return [[NSString stringWithUTF8String:page] stringByAppendingString:path];
 }
 
+static _finline void UpdateExternalStatus(uint64_t newStatus) {
+    int notify_token;
+    if (notify_register_check("com.saurik.Cydia.status", &notify_token) == NOTIFY_STATUS_OK) {
+        notify_set_state(notify_token, newStatus);
+        notify_cancel(notify_token);
+    }
+    notify_post("com.saurik.Cydia.status");
+}
+
 /* [NSObject yieldToSelector:(withObject:)] {{{*/
 @interface NSObject (Cydia)
 - (id) yieldToSelector:(SEL)selector withObject:(id)object;
@@ -277,7 +293,7 @@ static _finline NSString *CydiaURL(NSString *path) {
 @end
 /* }}} */
 
-@interface CYActionSheet : UIActionSheet {
+@interface CYActionSheet : UIAlertView {
     unsigned button_;
 }
 
@@ -287,20 +303,38 @@ static _finline NSString *CydiaURL(NSString *path) {
 @implementation CYActionSheet
 
 - (id) initWithTitle:(NSString *)title buttons:(NSArray *)buttons defaultButtonIndex:(int)index {
-    if ((self = [super initWithTitle:title buttons:buttons defaultButtonIndex:index delegate:self context:nil]) != nil) {
+    if ((self = [super init])) {
+        [self setTitle:title];
+        [self setDelegate:self];
+        for (NSString *button in buttons) [self addButtonWithTitle:button];
+        [self setCancelButtonIndex:index];
     } return self;
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    button_ = button;
+- (void) _updateFrameForDisplay {
+    [super _updateFrameForDisplay];
+    if ([self cancelButtonIndex] == -1) {
+        NSArray *buttons = [self buttons];
+        if ([buttons count]) {
+            UIImage *background = [[buttons objectAtIndex:0] backgroundForState:0];
+            for (UIThreePartButton *button in buttons)
+                [button setBackground:background forState:0];
+        }
+    }
+}
+
+- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
+    button_ = buttonIndex + 1;
+}
+
+- (void) dismiss {
+    [self dismissWithClickedButtonIndex:-1 animated:YES];
 }
 
 - (int) yieldToPopupAlertAnimated:(BOOL)animated {
+    [self setRunsModal:YES];
     button_ = 0;
-    [self popupAlertAnimated:animated];
-    NSRunLoop *loop([NSRunLoop currentRunLoop]);
-    NSDate *future([NSDate distantFuture]);
-    while (button_ == 0 && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
+    [self show];
     return button_;
 }
 
@@ -334,39 +368,6 @@ static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive |
     [self setObject:info forKey:[info objectForKey:@"CFBundleIdentifier"]];
 }
 
-@end
-/* }}} */
-/* Pop Transitions {{{ */
-@interface PopTransitionView : UITransitionView {
-}
-
-@end
-
-@implementation PopTransitionView
-
-- (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to {
-    if (from != nil && to == nil)
-        [self removeFromSuperview];
-}
-
-@end
-
-@implementation UIView (PopUpView)
-
-- (void) popFromSuperviewAnimated:(BOOL)animated {
-    [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
-}
-
-- (void) popSubview:(UIView *)view {
-    UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
-    [transition setDelegate:transition];
-    [self addSubview:transition];
-
-    UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
-    [transition transition:UITransitionNone toView:blank];
-    [transition transition:UITransitionPushFromBottom toView:view];
-}
-
 @end
 /* }}} */
 
@@ -376,10 +377,10 @@ static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive |
 #define TraceLogging (1 && !ForRelease)
 #define HistogramInsertionSort (0 && !ForRelease)
 #define ProfileTimes (0 && !ForRelease)
-#define ForSaurik (1 && !ForRelease)
+#define ForSaurik (0 && !ForRelease)
 #define LogBrowser (0 && !ForRelease)
 #define TrackResize (0 && !ForRelease)
-#define ManualRefresh (1 && !ForRelease)
+#define ManualRefresh (0 && !ForRelease)
 #define ShowInternals (0 && !ForRelease)
 #define IgnoreInstall (0 && !ForRelease)
 #define RecycleWebViews 0
@@ -525,7 +526,7 @@ CFIndex SKBSearch_(const void *element, CFIndex elementSize, const void *list, C
         CFIndex half = count / 2;
         const char *probe = ptr + elementSize * half;
         CFComparisonResult cr = comparator(element, probe, context);
-       if (0 == cr) return (probe - (const char *)list) / elementSize;
+    if (0 == cr) return (probe - (const char *)list) / elementSize;
         ptr = (cr < 0) ? ptr : probe + elementSize;
         count = (cr < 0) ? half : (half + (count & 1) - 1);
     }
@@ -538,7 +539,7 @@ CFIndex CFBSearch_(const void *element, CFIndex elementSize, const void *list, C
         CFIndex half = count / 2;
         const char *probe = ptr + elementSize * half;
         CFComparisonResult cr = comparator(element, probe, context);
-       if (0 == cr) return (probe - (const char *)list) / elementSize;
+    if (0 == cr) return (probe - (const char *)list) / elementSize;
         ptr = (cr < 0) ? ptr : probe + elementSize;
         count = (cr < 0) ? half : (half + (count & 1) - 1);
     }
@@ -617,6 +618,23 @@ void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFCompar
 @end
 /* }}} */
 
+@implementation WebScriptObject (NSFastEnumeration)
+
+- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)objects count:(NSUInteger)count {
+    size_t length([self count] - state->state);
+    if (length <= 0)
+        return 0;
+    else if (length > count)
+        length = count;
+    for (size_t i(0); i != length; ++i)
+        objects[i] = [self objectAtIndex:state->state++];
+    state->itemsPtr = objects;
+    state->mutationsPtr = (unsigned long *) self;
+    return length;
+}
+
+@end
+
 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
     size_t length([self length] - state->state);
     if (length <= 0)
@@ -630,10 +648,6 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
     return length;
 }
 
-@interface NSString (UIKit)
-- (NSString *) stringByAddingPercentEscapes;
-@end
-
 /* Cydia NSString Additions {{{ */
 @interface NSString (Cydia)
 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
@@ -692,17 +706,14 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
 
 - (NSString *) stringByCachingURLWithCurrentCDN {
     return [self
-        stringByReplacingOccurrencesOfString:@"://"
-        withString:@"://ne.edgecastcdn.net/8003A4/"
-        options:0
-        /* XXX: this is somewhat inaccurate */
-        range:NSMakeRange(0, 10)
+        stringByReplacingOccurrencesOfString:@"://cydia.saurik.com/"
+        withString:@"://cache.cydia.saurik.com/"
     ];
 }
 
 - (NSString *) stringByAddingPercentEscapesIncludingReserved {
     return [(id)CFURLCreateStringByAddingPercentEscapes(
-        kCFAllocatorDefault, 
+        kCFAllocatorDefault,
         (CFStringRef) self,
         NULL,
         CFSTR(";/?:@&=+$,"),
@@ -797,6 +808,8 @@ class CYString {
             if (size_ == 0)
                 return nil;
             cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<uint8_t *>(data_), size_, kCFStringEncodingUTF8, NO, kCFAllocatorNull);
+            if (cache_ == NULL)
+                cache_ = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<uint8_t *>(data_), size_, kCFStringEncodingISOLatin1, NO, kCFAllocatorNull);
         } return cache_;
     }
 
@@ -960,17 +973,17 @@ class Pcre {
 @end
 /* }}} */
 /* CoreGraphics Primitives {{{ */
-class CGColor {
+class CYColor {
   private:
     CGColorRef color_;
 
   public:
-    CGColor() :
+    CYColor() :
         color_(NULL)
     {
     }
 
-    CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
+    CYColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
         color_(NULL)
     {
         Set(space, red, green, blue, alpha);
@@ -981,14 +994,14 @@ class CGColor {
             CGColorRelease(color_);
     }
 
-    ~CGColor() {
+    ~CYColor() {
         Clear();
     }
 
     void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
         Clear();
         float color[] = {red, green, blue, alpha};
-        color_ = CGColorCreate(space, color);
+        color_ = CGColorCreate(space, (CGFloat *) color);
     }
 
     operator CGColorRef() {
@@ -999,6 +1012,7 @@ class CGColor {
 
 /* Random Global Variables {{{ */
 static const int PulseInterval_ = 50000;
+static const int ButtonBarWidth_ = 60;
 static const int ButtonBarHeight_ = 48;
 static const float KeyboardTime_ = 0.3f;
 
@@ -1010,15 +1024,15 @@ static NSArray *Finishes_;
 
 static bool Queuing_;
 
-static CGColor Blue_;
-static CGColor Blueish_;
-static CGColor Black_;
-static CGColor Off_;
-static CGColor White_;
-static CGColor Gray_;
-static CGColor Green_;
-static CGColor Purple_;
-static CGColor Purplish_;
+static CYColor Blue_;
+static CYColor Blueish_;
+static CYColor Black_;
+static CYColor Off_;
+static CYColor White_;
+static CYColor Gray_;
+static CYColor Green_;
+static CYColor Purple_;
+static CYColor Purplish_;
 
 static UIColor *InstallingColor_;
 static UIColor *RemovingColor_;
@@ -1036,20 +1050,19 @@ static UIFont *Font18Bold_;
 static UIFont *Font22Bold_;
 
 static const char *Machine_ = NULL;
-static const NSString *System_ = NULL;
-static const NSString *SerialNumber_ = nil;
-static const NSString *ChipID_ = nil;
-static const NSString *UniqueID_ = nil;
-static const NSString *Build_ = nil;
-static const NSString *Product_ = nil;
-static const NSString *Safari_ = nil;
+static NSString *System_ = nil;
+static NSString *SerialNumber_ = nil;
+static NSString *ChipID_ = nil;
+static NSString *Token_ = nil;
+static NSString *UniqueID_ = nil;
+static NSString *Build_ = nil;
+static NSString *Product_ = nil;
+static NSString *Safari_ = nil;
 
 static CFLocaleRef Locale_;
 static NSArray *Languages_;
 static CGColorSpaceRef space_;
 
-static bool reload_;
-
 static NSDictionary *SectionMap_;
 static NSMutableDictionary *Metadata_;
 static _transient NSMutableDictionary *Settings_;
@@ -1060,6 +1073,8 @@ static _transient NSMutableDictionary *Sources_;
 static bool Changed_;
 static NSDate *now_;
 
+static bool IsWildcat_;
+
 #if RecycleWebViews
 static NSMutableArray *Documents_;
 #endif
@@ -1162,6 +1177,8 @@ bool isSectionVisible(NSString *section) {
     return hidden == nil || ![hidden boolValue];
 }
 
+@class Cydia;
+
 /* Delegate Prototypes {{{ */
 @class Package;
 @class Source;
@@ -1183,22 +1200,26 @@ bool isSectionVisible(NSString *section) {
 - (void) setConfigurationData:(NSString *)data;
 @end
 
-@class PackageView;
+@class PackageController;
 
 @protocol CydiaDelegate
-- (void) setPackageView:(PackageView *)view;
+- (void) setPackageController:(PackageController *)view;
 - (void) clearPackage:(Package *)package;
 - (void) installPackage:(Package *)package;
+- (void) installPackages:(NSArray *)packages;
 - (void) removePackage:(Package *)package;
-- (void) slideUp:(UIActionSheet *)alert;
+- (void) beginUpdate;
+- (BOOL) updating;
 - (void) distUpgrade;
+- (void) loadData;
 - (void) updateData;
 - (void) syncData;
-- (void) askForSettings;
+- (void) showSettings;
 - (UIProgressHUD *) addProgressHUD;
 - (void) removeProgressHUD:(UIProgressHUD *)hud;
-- (RVPage *) pageForPackage:(NSString *)name;
-- (PackageView *) packageView;
+- (CYViewController *) pageForPackage:(NSString *)name;
+- (PackageController *) packageController;
+- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item;
 @end
 /* }}} */
 
@@ -1389,7 +1410,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 @end
 /* }}} */
 /* Delegate Helpers {{{ */
-@implementation NSObject(ProgressDelegate)
+@implementation NSObject (ProgressDelegate)
 
 - (void) _setProgressErrorPackage:(NSArray *)args {
     [self performSelector:@selector(setProgressError:forPackage:)
@@ -1414,8 +1435,11 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 
 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
     Package *package = id == nil ? nil : [[Database sharedInstance] packageWithName:id];
-    // XXX: holy typecast batman!
-    [(id<ProgressDelegate>)self setProgressError:error withTitle:(package == nil ? id : [package name])];
+    
+    [self performSelector:@selector(setProgressError:withTitle:)
+        withObject:error
+        withObject:(package == nil ? id : [package name])
+    ];
 }
 
 @end
@@ -1725,6 +1749,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
     bool essential_;
     bool required_;
     bool visible_;
+    bool obsolete_;
 
     NSString *latest_;
     CYString installed_;
@@ -1834,6 +1859,7 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 - (void) remove;
 
 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
+- (bool) isUnfilteredAndSelectedForBy:(NSString *)search;
 - (bool) isInstalledAndVisible:(NSNumber *)number;
 - (bool) isVisibleInSection:(NSString *)section;
 - (bool) isVisibleInSource:(Source *)source;
@@ -2087,7 +2113,7 @@ struct PackageNameOrdering :
 }
 
 - (void) setVisible {
-    visible_ = required_ && [self hasSupportingRole] && [self unfiltered];
+    visible_ = required_ && [self unfiltered];
 }
 
 - (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
@@ -2186,13 +2212,12 @@ struct PackageNameOrdering :
                 if (version == nil) {
                     [metadata_ setObject:latest_ forKey:@"LastVersion"];
                     changed = true;
-                } else {
-                if (![version isEqualToString:latest_]) {
+                } else if (![version isEqualToString:latest_]) {
                     [metadata_ setObject:latest_ forKey:@"LastVersion"];
                     lastSeen_ = now_;
                     [metadata_ setObject:lastSeen_ forKey:@"LastSeen"];
                     changed = true;
-                } }
+                }
             }
 
             metadata_ = [metadata_ retain];
@@ -2207,6 +2232,7 @@ struct PackageNameOrdering :
             section_.set(pool_, iterator_.Section());
         _end
 
+        obsolete_ = [self hasTag:@"cydia::obsolete"];
         essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
         [self setVisible];
     } _end } return self;
@@ -2388,7 +2414,7 @@ struct PackageNameOrdering :
 
 - (BOOL) unfiltered {
     NSString *section([self section]);
-    return section == nil || isSectionVisible(section);
+    return !obsolete_ && [self hasSupportingRole] && (section == nil || isSectionVisible(section));
 }
 
 - (BOOL) visible {
@@ -2461,11 +2487,13 @@ struct PackageNameOrdering :
     UIImage *icon(nil);
     if (!icon_.empty())
         if ([icon_ hasPrefix:@"file:///"])
+            // XXX: correct escaping
             icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]];
     if (icon == nil) if (section != nil)
         icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
     if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon])
         if ([dicon hasPrefix:@"file:///"])
+            // XXX: correct escaping
             icon = [UIImage imageAtPath:[dicon substringFromIndex:7]];
     if (icon == nil)
         icon = [UIImage applicationImageNamed:@"unknown.png"];
@@ -2770,6 +2798,25 @@ struct PackageNameOrdering :
     _end
 }
 
+- (bool) isUnfilteredAndSelectedForBy:(NSString *)search {
+    if ([search length] == 0)
+        return false;
+
+    _profile(Package$isUnfilteredAndSelectedForBy)
+        bool value(true);
+
+        _profile(Package$isUnfilteredAndSelectedForBy$Unfiltered)
+            value &= [self unfiltered];
+        _end
+
+        _profile(Package$isUnfilteredAndSelectedForBy$Match)
+            value &= [[self name] compare:search options:MatchCompareOptions_ range:NSMakeRange(0, [search length])] == NSOrderedSame;
+        _end
+
+        return value;
+    _end
+}
+
 - (bool) isInstalledAndVisible:(NSNumber *)number {
     return (![number boolValue] || [self visible]) && ![self uninstalled];
 }
@@ -3029,7 +3076,7 @@ static NSString *Warning_;
     return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:pool_ database:self];
 } }
 
-- (Database *) init {
+- (id) init {
     if ((self = [super init]) != nil) {
         policy_ = NULL;
         records_ = NULL;
@@ -3537,7 +3584,8 @@ static NSString *Warning_;
         return;
 
     if ([self popErrorWithTitle:title forOperation:ListUpdate(status, list, PulseInterval_)])
-        /* XXX: ignore this because users suck and don't understand why refreshing is important: return */;
+        /* XXX: ignore this because users suck and don't understand why refreshing is important: return */
+        /* XXX: why the hell is an empty if statement a clang error? */ (void) 0;
 
     [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
     Changed_ = true;
@@ -3557,7 +3605,7 @@ static NSString *Warning_;
 @end
 /* }}} */
 
-/* Confirmation View {{{ */
+/* Confirmation Controller {{{ */
 bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (!iterator.end())
         for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
@@ -3572,10 +3620,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     return false;
 }
+/* }}} */
 
 /* Web Scripting {{{ */
 @interface CydiaObject : NSObject {
     id indirect_;
+    id delegate_;
 }
 
 - (id) initWithDelegate:(IndirectDelegate *)indirect;
@@ -3594,6 +3644,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     } return self;
 }
 
+- (void) setDelegate:(id)delegate {
+    delegate_ = delegate;
+}
+
 + (NSArray *) _attributeKeys {
     return [NSArray arrayWithObjects:@"device", @"firewire", @"imei", @"mac", @"serial", nil];
 }
@@ -3639,6 +3693,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         return @"getInstalledPackages";
     else if (selector == @selector(getPackageById:))
         return @"getPackageById";
+    else if (selector == @selector(installPackages:))
+        return @"installPackages";
     else if (selector == @selector(setAutoPopup:))
         return @"setAutoPopup";
     else if (selector == @selector(setButtonImage:withStyle:toFunction:))
@@ -3651,6 +3707,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         return @"setPopupHook";
     else if (selector == @selector(setSpecial:))
         return @"setSpecial";
+    else if (selector == @selector(setToken:))
+        return @"setToken";
     else if (selector == @selector(setViewportWidth:))
         return @"setViewportWidth";
     else if (selector == @selector(supports:))
@@ -3678,7 +3736,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (NSArray *) getInstalledPackages {
     NSArray *packages([[Database sharedInstance] packages]);
     NSMutableArray *installed([NSMutableArray arrayWithCapacity:[packages count]]);
-    for (Package *package in installed)
+    for (Package *package in packages)
         if ([package installed] != nil)
             [installed addObject:package];
     return installed;
@@ -3751,6 +3809,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [indirect_ close];
 }
 
+- (void) installPackages:(NSArray *)packages {
+    [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO];
+}
+
 - (void) setAutoPopup:(BOOL)popup {
     [indirect_ setAutoPopup:popup];
 }
@@ -3767,6 +3829,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [indirect_ setSpecial:function];
 }
 
+- (void) setToken:(NSString *)token {
+    if (Token_ != nil)
+        [Token_ release];
+    Token_ = [token retain];
+
+    [Metadata_ setObject:Token_ forKey:@"Token"];
+    Changed_ = true;
+}
+
 - (void) setFinishHook:(id)function {
     [indirect_ setFinishHook:function];
 }
@@ -3785,7 +3856,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     id values[count];
     for (unsigned i(0); i != count; ++i)
         values[i] = [arguments objectAtIndex:i];
-    return [[[NSString alloc] initWithFormat:format arguments:reinterpret_cast<va_list>(values)] autorelease];
+    return [[[NSString alloc] initWithFormat:format arguments:*(reinterpret_cast<va_list *>(&values))] autorelease];
 }
 
 - (NSString *) localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)table {
@@ -3799,22 +3870,47 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-@interface CydiaBrowserView : BrowserView {
+/* Cydia Browser Controller {{{ */
+@interface CYBrowserController : BrowserController {
     CydiaObject *cydia_;
 }
 
 @end
 
-@implementation CydiaBrowserView
+@implementation CYBrowserController
 
 - (void) dealloc {
     [cydia_ release];
     [super dealloc];
 }
 
+- (void) setHeaders:(NSDictionary *)headers forHost:(NSString *)host {
+}
+
 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
     [super webView:sender didClearWindowObject:window forFrame:frame];
-    [window setValue:cydia_ forKey:@"cydia"];
+
+    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 (
+        [host isEqualToString:@"cydia.saurik.com"] ||
+        [host hasSuffix:@".cydia.saurik.com"] ||
+        [scheme isEqualToString:@"file"]
+    )
+        [window setValue:cydia_ forKey:@"cydia"];
 }
 
 - (void) _setMoreHeaders:(NSMutableURLRequest *)request {
@@ -3822,8 +3918,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [request setValue:System_ forHTTPHeaderField:@"X-System"];
     if (Machine_ != NULL)
         [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
-    if (UniqueID_ != nil)
-        [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
+    if (Token_ != nil)
+        [request setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"];
     if (Role_ != nil)
         [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
 }
@@ -3834,11 +3930,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return copy;
 }
 
-- (id) initWithBook:(RVBook *)book forWidth:(float)width {
-    if ((self = [super initWithBook:book forWidth:width ofClass:[CydiaBrowserView class]]) != nil) {
+- (void) setDelegate:(id)delegate {
+    [super setDelegate:delegate];
+    [cydia_ setDelegate:delegate];
+}
+
+- (id) init {
+    if ((self = [super initWithWidth:0 ofClass:[CYBrowserController class]]) != nil) {
         cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_];
 
-        WebView *webview([webview_ webView]);
+        WebView *webview([document_ webView]);
 
         Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
 
@@ -3859,27 +3960,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 @end
+/* }}} */
 
-@protocol ConfirmationViewDelegate
-- (void) cancel;
-- (void) confirm;
+/* Confirmation {{{ */
+@protocol ConfirmationControllerDelegate
+- (void) cancelAndClear:(bool)clear;
+- (void) confirmWithNavigationController:(UINavigationController *)navigation;
 - (void) queue;
 @end
 
-@interface ConfirmationView : CydiaBrowserView {
+@interface ConfirmationController : CYBrowserController {
     _transient Database *database_;
-    UIActionSheet *essential_;
+    UIAlertView *essential_;
     NSArray *changes_;
     NSArray *issues_;
     NSArray *sizes_;
     BOOL substrate_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 
 @end
 
-@implementation ConfirmationView
+@implementation ConfirmationController
 
 - (void) dealloc {
     [changes_ release];
@@ -3891,33 +3994,32 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (void) cancel {
-    [delegate_ cancel];
-    [book_ popFromSuperviewAnimated:YES];
-}
-
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
+- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+    NSString *context([alert context]);
 
     if ([context isEqualToString:@"remove"]) {
-        switch (button) {
-            case 1:
-                [self cancel];
-                break;
-            case 2:
-                if (substrate_)
-                    Finish_ = 2;
-                [delegate_ confirm];
-                break;
-            _nodefault
+        if (button == [alert cancelButtonIndex]) {
+            [self dismissModalViewControllerAnimated:YES];
+        } else if (button == [alert firstOtherButtonIndex]) {
+            if (substrate_)
+                Finish_ = 2;
+            [delegate_ confirmWithNavigationController:[self navigationController]];
         }
 
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     } else if ([context isEqualToString:@"unable"]) {
-        [self cancel];
-        [sheet dismiss];
-    } else
-        [super alertSheet:sheet buttonClicked:button];
+        [self dismissModalViewControllerAnimated:YES];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
+    } else {
+        [super alertView:alert clickedButtonAtIndex:button];
+    }
+}
+
+- (id) invokeDefaultMethodWithArguments:(NSArray *)args {
+    [self dismissModalViewControllerAnimated:YES];
+    [delegate_ cancelAndClear:NO];
+
+    return nil;
 }
 
 - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
@@ -3925,12 +4027,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [window setValue:changes_ forKey:@"changes"];
     [window setValue:issues_ forKey:@"issues"];
     [window setValue:sizes_ forKey:@"sizes"];
+    [window setValue:self forKey:@"queue"];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
         database_ = database;
 
+        [[self navigationItem] setTitle:UCLocalize("CONFIRM")];
+
         NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
         NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
         NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
@@ -3972,29 +4077,25 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         else if (Advanced_) {
             NSString *parenthetical(UCLocalize("PARENTHETICAL"));
 
-            essential_ = [[UIActionSheet alloc]
+            essential_ = [[UIAlertView alloc]
                 initWithTitle:UCLocalize("REMOVING_ESSENTIALS")
-                buttons:[NSArray arrayWithObjects:
-                    [NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")],
-                    [NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")],
-                nil]
-                defaultButtonIndex:0
+                message:UCLocalize("REMOVING_ESSENTIALS_EX")
                 delegate:self
-                context:@"remove"
+                cancelButtonTitle:[NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")]
+                otherButtonTitles:[NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")], nil
             ];
 
-            [essential_ setDestructiveButtonIndex:1];
-            [essential_ setBodyText:UCLocalize("REMOVING_ESSENTIALS_EX")];
+            [essential_ setContext:@"remove"];
         } else {
-            essential_ = [[UIActionSheet alloc]
+            essential_ = [[UIAlertView alloc]
                 initWithTitle:UCLocalize("UNABLE_TO_COMPLY")
-                buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil]
-                defaultButtonIndex:0
+                message:UCLocalize("UNABLE_TO_COMPLY_EX")
                 delegate:self
-                context:@"unable"
+                cancelButtonTitle:UCLocalize("OKAY")
+                otherButtonTitles:nil
             ];
 
-            [essential_ setBodyText:UCLocalize("UNABLE_TO_COMPLY_EX")];
+            [essential_ setContext:@"unable"];
         }
 
         changes_ = [[NSArray alloc] initWithObjects:
@@ -4012,48 +4113,54 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         sizes_ = [[NSArray alloc] initWithObjects:
             SizeString([database_ fetcher].FetchNeeded()),
             SizeString([database_ fetcher].PartialPresent()),
-            SizeString([database_ cache]->UsrSize()),
         nil];
 
         [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
-    } return self;
-}
-
-- (NSString *) backButtonTitle {
-    return UCLocalize("CONFIRM");
-}
-
-- (NSString *) leftButtonTitle {
-    return [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("CANCEL"), UCLocalize("QUEUE")];
-}
 
-- (id) rightButtonTitle {
-    return issues_ != nil ? nil : [super rightButtonTitle];
+        UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("CANCEL")
+            // OLD: [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("CANCEL"), UCLocalize("QUEUE")]
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(cancelButtonClicked)
+        ];
+        [[self navigationItem] setLeftBarButtonItem:leftItem];
+        [leftItem release];
+    } return self;
 }
 
-- (id) _rightButtonTitle {
-#if AlwaysReload || IgnoreInstall
-    return [super _rightButtonTitle];
+- (void) applyRightButton {
+    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
+        initWithTitle:UCLocalize("CONFIRM")
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(confirmButtonClicked)
+    ];
+#if !AlwaysReload && !IgnoreInstall
+    if (issues_ == nil && ![self isLoading]) [[self navigationItem] setRightBarButtonItem:rightItem];
+    else [super applyRightButton];
 #else
-    return UCLocalize("CONFIRM");
+    [[self navigationItem] setRightBarButtonItem:nil];
 #endif
+    [rightItem release];
 }
 
-- (void) _leftButtonClicked {
-    [self cancel];
+- (void) cancelButtonClicked {
+    [self dismissModalViewControllerAnimated:YES];
+    [delegate_ cancelAndClear:YES];
 }
 
 #if !AlwaysReload
-- (void) _rightButtonClicked {
+- (void) confirmButtonClicked {
 #if IgnoreInstall
-    return [super _rightButtonClicked];
+    return;
 #endif
     if (essential_ != nil)
-        [essential_ popupAlertAnimated:YES];
+        [essential_ show];
     else {
         if (substrate_)
             Finish_ = 2;
-        [delegate_ confirm];
+        [delegate_ confirmWithNavigationController:[self navigationController]];
     }
 }
 #endif
@@ -4099,31 +4206,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 /* }}} */
-/* Progress View {{{ */
-@interface ProgressView : UIView <
+/* Progress Controller {{{ */
+@interface ProgressController : CYViewController <
     ConfigurationDelegate,
     ProgressDelegate
 > {
     _transient Database *database_;
-    UIView *view_;
-    UIView *background_;
-    UITransitionView *transition_;
-    UIView *overlay_;
-    UINavigationBar *navbar_;
     UIProgressBar *progress_;
     UITextView *output_;
     UITextLabel *status_;
     UIPushButton *close_;
-    id delegate_;
     BOOL running_;
     SHA1SumValue springlist_;
     SHA1SumValue notifyconf_;
     NSString *title_;
 }
 
-- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
-- (void) setContentView:(UIView *)view;
-- (void) resetView;
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate;
 
 - (void) _retachThread;
 - (void) _detachNewThreadData:(ProgressData *)data;
@@ -4133,22 +4232,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 
-@protocol ProgressViewDelegate
-- (void) progressViewIsComplete:(ProgressView *)sender;
+@protocol ProgressControllerDelegate
+- (void) progressControllerIsComplete:(ProgressController *)sender;
 @end
 
-@implementation ProgressView
+@implementation ProgressController
 
 - (void) dealloc {
-    [transition_ setDelegate:nil];
-    [navbar_ setDelegate:nil];
-
-    [view_ release];
-    if (background_ != nil)
-        [background_ release];
-    [transition_ release];
-    [overlay_ release];
-    [navbar_ release];
+    [database_ setDelegate:nil];
     [progress_ release];
     [output_ release];
     [status_ release];
@@ -4158,134 +4249,114 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
-    if ((self = [super initWithFrame:frame]) != nil) {
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
+    if ((self = [super init]) != nil) {
         database_ = database;
+        [database_ setDelegate:self];
         delegate_ = delegate;
 
-        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
-        [transition_ setDelegate:self];
+        [[self view] setBackgroundColor:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]];
 
-        overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
-
-        background_ = [[UIView alloc] initWithFrame:[self bounds]];
-        [background_ setBackgroundColor:[UIColor blackColor]];
-        [self addSubview:background_];
-
-        [self addSubview:transition_];
-
-        CGSize navsize = [UINavigationBar defaultSize];
-        CGRect navrect = {{0, 0}, navsize};
-
-        navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
-        [overlay_ addSubview:navbar_];
-
-        [navbar_ setBarStyle:1];
-        [navbar_ setDelegate:self];
-
-        UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
-        [navbar_ pushNavigationItem:navitem];
-
-        CGRect bounds = [overlay_ bounds];
-        CGSize prgsize = [UIProgressBar defaultSize];
-
-        CGRect prgrect = {{
-            (bounds.size.width - prgsize.width) / 2,
-            bounds.size.height - prgsize.height - 20
-        }, prgsize};
-
-        progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
+        progress_ = [[UIProgressBar alloc] init];
+        [progress_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)];
         [progress_ setStyle:0];
 
-        status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
-            10,
-            bounds.size.height - prgsize.height - 50,
-            bounds.size.width - 20,
-            24
-        )];
-
+        status_ = [[UITextLabel alloc] init];
+        [status_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)];
         [status_ setColor:[UIColor whiteColor]];
         [status_ setBackgroundColor:[UIColor clearColor]];
-
         [status_ setCentersHorizontally:YES];
         //[status_ setFont:font];
 
-        output_ = [[UITextView alloc] initWithFrame:CGRectMake(
-            10,
-            navrect.size.height + 20,
-            bounds.size.width - 20,
-            bounds.size.height - navsize.height - 62 - navrect.size.height
-        )];
+        output_ = [[UITextView alloc] init];
 
+        [output_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
         //[output_ setTextFont:@"Courier New"];
         [output_ setFont:[[output_ font] fontWithSize:12]];
-
         [output_ setTextColor:[UIColor whiteColor]];
         [output_ setBackgroundColor:[UIColor clearColor]];
-
         [output_ setMarginTop:0];
         [output_ setAllowsRubberBanding:YES];
         [output_ setEditable:NO];
+        [[self view] addSubview:output_];
 
-        [overlay_ addSubview:output_];
-
-        close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
-            10,
-            bounds.size.height - prgsize.height - 50,
-            bounds.size.width - 20,
-            32 + prgsize.height
-        )];
-
+        close_ = [[UIPushButton alloc] init];
+        [close_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin)];
         [close_ setAutosizesToFit:NO];
         [close_ setDrawsShadow:YES];
         [close_ setStretchBackground:YES];
         [close_ setEnabled:YES];
-
-        UIFont *bold = [UIFont boldSystemFontOfSize:22];
-        [close_ setTitleFont:bold];
-
+        [close_ setTitleFont:[UIFont boldSystemFontOfSize:22]];
         [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:UIControlEventTouchUpInside];
         [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
         [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
     } return self;
 }
 
-- (void) setContentView:(UIView *)view {
-    view_ = [view retain];
+- (void) positionViews {
+    CGRect bounds = [[self view] bounds];
+    CGSize prgsize = [UIProgressBar defaultSize];
+
+    CGRect prgrect = {{
+        (bounds.size.width - prgsize.width) / 2,
+        bounds.size.height - prgsize.height - 64
+    }, prgsize};
+
+    float closewidth = bounds.size.width - 20;
+    if (closewidth > 300) closewidth = 300;
+
+    [progress_ setFrame:prgrect];
+    [status_ setFrame:CGRectMake(
+        10,
+        bounds.size.height - prgsize.height - 94,
+        bounds.size.width - 20,
+        24
+    )];
+    [output_ setFrame:CGRectMake(
+        10,
+        20,
+        bounds.size.width - 20,
+        bounds.size.height - 106
+    )];
+    [close_ setFrame:CGRectMake(
+        (bounds.size.width - closewidth) / 2,
+        bounds.size.height - prgsize.height - 94,
+        closewidth,
+        32 + prgsize.height
+    )];
 }
 
-- (void) resetView {
-    [transition_ transition:6 toView:view_];
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [[self navigationItem] setHidesBackButton:YES];
+    [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack];
+
+    [self positionViews];
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+    [self positionViews];
+}
+
+- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+    NSString *context([alert context]);
 
     if ([context isEqualToString:@"conffile"]) {
         FILE *input = [database_ input];
-
-        switch (button) {
-            case 1:
-                fprintf(input, "N\n");
-                fflush(input);
-                break;
-            case 2:
-                fprintf(input, "Y\n");
-                fflush(input);
-                break;
-            _nodefault
-        }
-
-        [sheet dismiss];
+        if (button == [alert cancelButtonIndex]) fprintf(input, "N\n");
+        else if (button == [alert firstOtherButtonIndex]) fprintf(input, "Y\n");
+        fflush(input);
     }
 }
 
 - (void) closeButtonPushed {
     running_ = NO;
 
+    UpdateExternalStatus(0);
+
     switch (Finish_) {
         case 0:
-            [self resetView];
+            [self dismissModalViewControllerAnimated:YES];
         break;
 
         case 1:
@@ -4311,15 +4382,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) _retachThread {
-    UINavigationItem *item([navbar_ topItem]);
-    [item setTitle:UCLocalize("COMPLETE")];
+    [[self navigationItem] setTitle:UCLocalize("COMPLETE")];
 
-    [overlay_ addSubview:close_];
+    [[self view] addSubview:close_];
     [progress_ removeFromSuperview];
     [status_ removeFromSuperview];
 
     [database_ popErrorWithTitle:title_];
-    [delegate_ progressViewIsComplete:self];
+    [delegate_ progressControllerIsComplete:self];
 
     if (Finish_ < 4) {
         FileFd file;
@@ -4348,62 +4418,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 
     switch (Finish_) {
-        case 0: [close_ setTitle:UCLocalize("RETURN_TO_CYDIA")]; break;
+        case 0: [close_ setTitle:UCLocalize("RETURN_TO_CYDIA")]; break; /* XXX: Maybe UCLocalize("DONE")? */
         case 1: [close_ setTitle:UCLocalize("CLOSE_CYDIA")]; break;
         case 2: [close_ setTitle:UCLocalize("RESTART_SPRINGBOARD")]; break;
         case 3: [close_ setTitle:UCLocalize("RELOAD_SPRINGBOARD")]; break;
         case 4: [close_ setTitle:UCLocalize("REBOOT_DEVICE")]; break;
     }
 
-#define ListCache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
-#define IconCache_ "/User/Library/Caches/com.apple.springboard-imagecache-icons.plist"
-
-    unlink(IconCache_);
-
-    if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ListCache_]) {
-        [cache autorelease];
-
-        NSFileManager *manager([NSFileManager defaultManager]);
-        NSError *error(nil);
-
-        id system([cache objectForKey:@"System"]);
-        if (system == nil)
-            goto error;
-
-        struct stat info;
-        if (stat(ListCache_, &info) == -1)
-            goto error;
-
-        [system removeAllObjects];
-
-        if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
-            for (NSString *app in apps)
-                if ([app hasSuffix:@".app"]) {
-                    NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
-                    NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
-                    if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
-                        [info autorelease];
-                        if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
-                            [info setObject:path forKey:@"Path"];
-                            [info setObject:@"System" forKey:@"ApplicationType"];
-                            [system addInfoDictionary:info];
-                        }
-                    }
-                }
-        } else goto error;
-
-        [cache writeToFile:@ListCache_ atomically:YES];
-
-        if (chown(ListCache_, info.st_uid, info.st_gid) == -1)
-            goto error;
-        if (chmod(ListCache_, info.st_mode) == -1)
-            goto error;
-
-        if (false) error:
-            lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
-    }
+    system("su -c /usr/bin/uicache mobile");
 
-    notify_post("com.apple.mobile.application_installed");
+    UpdateExternalStatus(Finish_ == 0 ? 2 : 0);
 
     [delegate_ setStatusBarShowsProgress:NO];
 }
@@ -4416,6 +4440,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
+    UpdateExternalStatus(1);
+
     if (title_ != nil)
         [title_ release];
     if (title == nil)
@@ -4423,16 +4449,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     else
         title_ = [title retain];
 
-    UINavigationItem *item([navbar_ topItem]);
-    [item setTitle:title_];
+    [[self navigationItem] setTitle:title_];
 
     [status_ setText:nil];
     [output_ setText:@""];
     [progress_ setProgress:0];
 
     [close_ removeFromSuperview];
-    [overlay_ addSubview:progress_];
-    [overlay_ addSubview:status_];
+    [[self view] addSubview:progress_];
+    [[self view] addSubview:status_];
 
     [delegate_ setStatusBarShowsProgress:YES];
     running_ = YES;
@@ -4461,8 +4486,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         }
     }
 
-    [transition_ transition:6 toView:overlay_];
-
     [NSThread
         detachNewThreadSelector:@selector(_detachNewThreadData:)
         toTarget:self
@@ -4498,7 +4521,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         defaultButtonIndex:0
     ] autorelease]);
 
-    [sheet setBodyText:error];
+    [sheet setMessage:error];
     [sheet yieldToPopupAlertAnimated:YES];
     [sheet dismiss];
 }
@@ -4545,20 +4568,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     NSString *ofile = conffile_r[1];
     //NSString *nfile = conffile_r[2];
 
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
+    UIAlertView *alert = [[[UIAlertView alloc]
         initWithTitle:UCLocalize("CONFIGURATION_UPGRADE")
-        buttons:[NSArray arrayWithObjects:
-            UCLocalize("KEEP_OLD_COPY"),
-            UCLocalize("ACCEPT_NEW_COPY"),
-            // XXX: UCLocalize("SEE_WHAT_CHANGED"),
-        nil]
-        defaultButtonIndex:0
+        message:[NSString stringWithFormat:@"%@\n\n%@", UCLocalize("CONFIGURATION_UPGRADE_EX"), ofile]
         delegate:self
-        context:@"conffile"
+        cancelButtonTitle:UCLocalize("KEEP_OLD_COPY")
+        otherButtonTitles:UCLocalize("ACCEPT_NEW_COPY"),
+        // XXX: UCLocalize("SEE_WHAT_CHANGED"),
+        nil
     ] autorelease];
 
-    [sheet setBodyText:[NSString stringWithFormat:@"%@\n\n%@", UCLocalize("CONFIGURATION_UPGRADE_EX"), ofile]];
-    [sheet popupAlertAnimated:YES];
+    [alert setContext:@"conffile"];
+    [alert show];
 }
 
 - (void) _setProgressTitle:(NSString *)title {
@@ -4590,14 +4611,39 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Package Cell {{{ */
+/* Cell Content View {{{ */
+@protocol ContentDelegate
+- (void) drawContentRect:(CGRect)rect;
+@end
+
 @interface ContentView : UIView {
-    _transient id delegate_;
+    _transient id<ContentDelegate> delegate_;
 }
 
 @end
 
-@interface PackageCell : UITableViewCell {
+@implementation ContentView
+- (id) initWithFrame:(CGRect)frame {
+    if ((self = [super initWithFrame:frame]) != nil) {
+        /* Fix landscape stretching. */
+        [self setNeedsDisplayOnBoundsChange:YES];
+    } return self;
+}
+
+- (void) setDelegate:(id<ContentDelegate>)delegate {
+    delegate_ = delegate;
+}
+
+- (void) drawRect:(CGRect)rect {
+    [super drawRect:rect];
+    [delegate_ drawContentRect:rect];
+}
+@end
+/* }}} */
+/* Package Cell {{{ */
+@interface PackageCell : UITableViewCell <
+    ContentDelegate
+> {
     UIImage *icon_;
     NSString *name_;
     NSString *description_;
@@ -4620,24 +4666,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 
-@implementation ContentView
-
-- (id) initWithFrame:(CGRect)frame {
-    if ((self = [super initWithFrame:frame]) != nil) {
-    } return self;
-}
-
-- (void) setDelegate:(id)delegate {
-    delegate_ = delegate;
-}
-
-- (void) drawRect:(CGRect)rect {
-    [super drawRect:rect];
-    [delegate_ drawContentRect:rect];
-}
-
-@end
-
 @implementation PackageCell
 
 - (void) clearPackage {
@@ -4691,11 +4719,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if ((self = [super initWithFrame:frame reuseIdentifier:@"Package"]) != nil) {
         UIView *content([self contentView]);
         CGRect bounds([content bounds]);
+
         content_ = [[ContentView alloc] initWithFrame:bounds];
+        [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [content addSubview:content_];
+
         [content_ setDelegate:self];
-        [content_ setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
         [content_ setOpaque:YES];
-        [content addSubview:content_];
         if ([self respondsToSelector:@selector(selectionPercent)])
             faded_ = YES;
     } return self;
@@ -4721,7 +4751,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     icon_ = [[package icon] retain];
     name_ = [[package name] retain];
-    description_ = [[package shortDescription] retain];
+
+    if (IsWildcat_)
+        description_ = [package longDescription];
+    if (description_ == nil)
+        description_ = [package shortDescription];
+    if (description_ != nil)
+        description_ = [description_ retain];
+
     commercial_ = [package isCommercial];
 
     package_ = [package retain];
@@ -4762,6 +4799,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) drawContentRect:(CGRect)rect {
     bool selected([self isSelected]);
+    float width([self bounds].size.width);
 
 #if 0
     CGContextRef context(UIGraphicsGetCurrentContext());
@@ -4796,15 +4834,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     if (!selected)
         UISetColor(commercial_ ? Purple_ : Black_);
-    [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(placard_ == nil ? 240 : 214) withFont:Font18Bold_ ellipsis:2];
-    [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
+    [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
+    [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
 
     if (!selected)
         UISetColor(commercial_ ? Purplish_ : Gray_);
-    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:274 withFont:Font14_ ellipsis:2];
+    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation];
 
     if (placard_ != nil)
-        [placard_ drawAtPoint:CGPointMake(268, 9)];
+        [placard_ drawAtPoint:CGPointMake(width - 52, 9)];
 }
 
 - (void) setSelected:(BOOL)selected animated:(BOOL)fade {
@@ -4820,17 +4858,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Section Cell {{{ */
-@interface SectionCell : UISimpleTableCell {
+@interface SectionCell : UITableViewCell <
+    ContentDelegate
+> {
     NSString *basic_;
     NSString *section_;
     NSString *name_;
     NSString *count_;
     UIImage *icon_;
-    _UISwitchSlider *switch_;
+    ContentView *content_;
+    id switch_;
     BOOL editing_;
 }
 
-- (id) init;
 - (void) setSection:(Section *)section editing:(BOOL)editing;
 
 @end
@@ -4839,7 +4879,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) clearSection {
     if (basic_ != nil) {
-        [basic_ release]
+        [basic_ release];
         basic_ = nil;
     }
 
@@ -4863,15 +4903,26 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self clearSection];
     [icon_ release];
     [switch_ release];
+    [content_ release];
+
     [super dealloc];
 }
 
-- (id) init {
-    if ((self = [super init]) != nil) {
+- (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
+    if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
         icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
+        switch_ = [[objc_getClass("UISwitch") alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
+        [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventValueChanged];
+
+        UIView *content([self contentView]);
+        CGRect bounds([content bounds]);
+
+        content_ = [[ContentView alloc] initWithFrame:bounds];
+        [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [content addSubview:content_];
+        [content_ setBackgroundColor:[UIColor whiteColor]];
 
-        switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
-        [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventTouchUpInside];
+        [content_ setDelegate:self];
     } return self;
 }
 
@@ -4883,7 +4934,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 
     Changed_ = true;
-    [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
+    [metadata setObject:[NSNumber numberWithBool:([switch_ isOn] == NO)] forKey:@"Hidden"];
 }
 
 - (void) setSection:(Section *)section editing:(BOOL)editing {
@@ -4901,7 +4952,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         name_ = [UCLocalize("ALL_PACKAGES") retain];
         count_ = nil;
     } else {
-        basic_ = [section name_];
+        basic_ = [section name];
         if (basic_ != nil)
             basic_ = [basic_ retain];
 
@@ -4913,11 +4964,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
 
         if (editing_)
-            [switch_ setValue:(isSectionVisible(basic_) ? 1 : 0) animated:NO];
+            [switch_ setOn:(isSectionVisible(basic_) ? 1 : 0) animated:NO];
     }
+
+    [self setAccessoryType:editing ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator];
+    [content_ setNeedsDisplay];
 }
 
-- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
+- (void) setFrame:(CGRect)frame {
+    [super setFrame:frame];
+
+    CGRect rect([switch_ frame]);
+    [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)];
+}
+
+- (void) drawContentRect:(CGRect)rect {
+    BOOL selected = [self isSelected];
+
     [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
 
     if (selected)
@@ -4925,30 +4988,36 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     if (!selected)
         UISetColor(Black_);
-    [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
+
+    float width(rect.size.width);
+    if (editing_)
+        width -= 87;
+
+    [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ lineBreakMode:UILineBreakModeTailTruncation];
 
     CGSize size = [count_ sizeWithFont:Font14_];
 
     UISetColor(White_);
     if (count_ != nil)
         [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
-
-    [super drawContentInRect:rect selected:selected];
 }
 
 @end
 /* }}} */
 
 /* File Table {{{ */
-@interface FileTable : RVPage {
+@interface FileTable : CYViewController <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
     _transient Database *database_;
     Package *package_;
     NSString *name_;
     NSMutableArray *files_;
-    UITable *list_;
+    UITableView *list_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 - (void) setPackage:(Package *)package;
 
 @end
@@ -4965,48 +5034,43 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (int) numberOfRowsInTable:(UITable *)table {
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
     return files_ == nil ? 0 : [files_ count];
 }
 
-- (float) table:(UITable *)table heightForRow:(int)row {
-    return 24;
-}
+/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+    return 24.0f;
+}*/
+
+- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    static NSString *reuseIdentifier = @"Cell";
 
-- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
-    if (reusing == nil) {
-        reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
-        UIFont *font = [UIFont systemFontOfSize:16];
-        [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
+    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+    if (cell == nil) {
+        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
+        [cell setFont:[UIFont systemFontOfSize:16]];
     }
-    [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
-    return reusing;
+    [cell setText:[files_ objectAtIndex:indexPath.row]];
+    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
+
+    return cell;
 }
 
-- (BOOL) table:(UITable *)table canSelectRow:(int)row {
-    return NO;
-}
-
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
         database_ = database;
 
-        files_ = [[NSMutableArray arrayWithCapacity:32] retain];
+        [[self navigationItem] setTitle:UCLocalize("INSTALLED_FILES")];
 
-        list_ = [[UITable alloc] initWithFrame:[self bounds]];
-        [self addSubview:list_];
+        files_ = [[NSMutableArray arrayWithCapacity:32] retain];
 
-        UITableColumn *column = [[[UITableColumn alloc]
-            initWithTitle:UCLocalize("NAME")
-            identifier:@"name"
-            width:[self frame].size.width
-        ] autorelease];
+        list_ = [[UITableView alloc] initWithFrame:[[self view] bounds]];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [list_ setRowHeight:24.0f];
+        [[self view] addSubview:list_];
 
         [list_ setDataSource:self];
-        [list_ setSeparatorStyle:1];
-        [list_ addTableColumn:column];
         [list_ setDelegate:self];
-        [list_ setReusesTableCells:YES];
     } return self;
 }
 
@@ -5055,27 +5119,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [list_ reloadData];
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
-}
-
 - (void) reloadData {
     [self setPackage:[database_ packageWithName:name_]];
-    [self reloadButtons];
-}
-
-- (NSString *) title {
-    return UCLocalize("INSTALLED_FILES");
-}
-
-- (NSString *) backButtonTitle {
-    return UCLocalize("FILES");
 }
 
 @end
 /* }}} */
-/* Package View {{{ */
-@interface PackageView : CydiaBrowserView {
+/* Package Controller {{{ */
+@interface PackageController : CYBrowserController <
+    UIActionSheetDelegate
+> {
     _transient Database *database_;
     Package *package_;
     NSString *name_;
@@ -5083,12 +5136,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     NSMutableArray *buttons_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 - (void) setPackage:(Package *)package;
 
 @end
 
-@implementation PackageView
+@implementation PackageController
 
 - (void) dealloc {
     if (package_ != nil)
@@ -5101,7 +5154,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) release {
     if ([self retainCount] == 1)
-        [delegate_ setPackageView:self];
+        [delegate_ setPackageController:self];
     [super release];
 }
 
@@ -5120,20 +5173,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     else _assert(false);
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
+- (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
     NSString *context([sheet context]);
 
     if ([context isEqualToString:@"modify"]) {
-        int count = [buttons_ count];
-        _assert(count != 0);
-        _assert(button <= count + 1);
-
-        if (count != button - 1)
-            [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
+        if (button != [sheet cancelButtonIndex]) {
+            NSString *buttonName = [buttons_ objectAtIndex:button];
+            [self _clickButtonWithName:buttonName];
+        }
 
-        [sheet dismiss];
-    } else
-        [super alertSheet:sheet buttonClicked:button];
+        [sheet dismissWithClickedButtonIndex:-1 animated:YES];
+    }
 }
 
 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
@@ -5150,7 +5200,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 #if !AlwaysReload
-- (void) __rightButtonClicked {
+- (void) _customButtonClicked {
     int count([buttons_ count]);
     if (count == 0)
         return;
@@ -5158,39 +5208,58 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (count == 1)
         [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
     else {
-        NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
+        NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:count];
         [buttons addObjectsFromArray:buttons_];
-        [buttons addObject:UCLocalize("CANCEL")];
 
-        [delegate_ slideUp:[[[UIActionSheet alloc]
+        UIActionSheet *sheet = [[[UIActionSheet alloc]
             initWithTitle:nil
-            buttons:buttons
-            defaultButtonIndex:([buttons count] - 1)
             delegate:self
-            context:@"modify"
-        ] autorelease]];
+            cancelButtonTitle:nil
+            destructiveButtonTitle:nil
+            otherButtonTitles:nil
+        ] autorelease];
+
+        for (NSString *button in buttons) [sheet addButtonWithTitle:button];
+        if (!IsWildcat_) {
+           [sheet addButtonWithTitle:UCLocalize("CANCEL")];
+           [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
+        }
+        [sheet setContext:@"modify"];
+
+        [delegate_ showActionSheet:sheet fromItem:[[self navigationItem] rightBarButtonItem]];
     }
 }
 
-- (void) _rightButtonClicked {
+// We don't want to allow non-commercial packages to do custom things to the install button,
+// so it must call customButtonClicked with a custom commercial_ == 1 fallthrough.
+- (void) customButtonClicked {
     if (commercial_)
-        [super _rightButtonClicked];
+        [super customButtonClicked];
     else
-        [self __rightButtonClicked];
+        [self _customButtonClicked];
 }
-#endif
 
-- (id) _rightButtonTitle {
-    int count = [buttons_ count];
-    return count == 0 ? nil : count != 1 ? UCLocalize("MODIFY") : [buttons_ objectAtIndex:0];
+- (void) reloadButtonClicked {
+    // Don't reload a package view by clicking the button.
+}
+
+- (void) applyLoadingTitle {
+    // Don't show "Loading" as the title. Ever.
 }
 
-- (NSString *) backButtonTitle {
-    return @"Details";
+- (UIBarButtonItem *) rightButton {
+    int count = [buttons_ count];
+    return [[[UIBarButtonItem alloc]
+        initWithTitle:count == 0 ? nil : count != 1 ? UCLocalize("MODIFY") : [buttons_ objectAtIndex:0]
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(customButtonClicked)
+    ] autorelease];
 }
+#endif
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (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"]]];
@@ -5230,15 +5299,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [buttons_ addObject:UCLocalize("REMOVE")];
 
         if (special_ != NULL) {
-            CGRect frame([webview_ frame]);
-            frame.size.width = 320;
+            CGRect frame([document_ frame]);
             frame.size.height = 0;
-            [webview_ setFrame:frame];
+            [document_ setFrame:frame];
 
-            [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
+            if ([scroller_ respondsToSelector:@selector(scrollPointVisibleAtTopLeft:)])
+                [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
+            else
+                [scroller_ scrollRectToVisible:CGRectZero animated:NO];
 
             WebThreadLock();
-            [[[webview_ webView] windowScriptObject] setValue:package_ forKey:@"package"];
+            [[[document_ webView] windowScriptObject] setValue:package_ forKey:@"package"];
 
             [self setButtonTitle:nil withStyle:nil toFunction:nil];
 
@@ -5250,8 +5321,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [super callFunction:special_];
         }
     }
-
-    [self reloadButtons];
 }
 
 - (bool) isLoading {
@@ -5265,17 +5334,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Package Table {{{ */
-@interface PackageTable : RVPage {
+@interface PackageTable : UIView <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
     _transient Database *database_;
-    NSString *title_;
     NSMutableArray *packages_;
     NSMutableArray *sections_;
     UITableView *list_;
     NSMutableArray *index_;
     NSMutableDictionary *indices_;
+    id target_;
+    SEL action_;
+    id delegate_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title;
+- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action;
 
 - (void) setDelegate:(id)delegate;
 
@@ -5286,19 +5360,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
 
+- (void) deselectWithAnimation:(BOOL)animated;
+
 @end
 
 @implementation PackageTable
 
 - (void) dealloc {
-    [list_ setDataSource:nil];
-
-    [title_ release];
     [packages_ release];
     [sections_ release];
     [list_ release];
     [index_ release];
     [indices_ release];
+
     [super dealloc];
 }
 
@@ -5327,25 +5401,25 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
-    PackageCell *cell([table dequeueReusableCellWithIdentifier:@"Package"]);
+    PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
     if (cell == nil)
         cell = [[[PackageCell alloc] init] autorelease];
     [cell setPackage:[self packageAtIndexPath:path]];
     return cell;
 }
 
-- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
-    return 73;
-    return [PackageCell heightForPackage:[self packageAtIndexPath:path]];
+- (void) deselectWithAnimation:(BOOL)animated {
+    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
 }
 
+/*- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
+    return [PackageCell heightForPackage:[self packageAtIndexPath:path]];
+}*/
+
 - (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
     Package *package([self packageAtIndexPath:path]);
     package = [database_ packageWithName:[package id]];
-    PackageView *view([delegate_ packageView]);
-    [view setPackage:package];
-    [view setDelegate:delegate_];
-    [book_ pushPage:view];
+    [target_ performSelector:action_ withObject:package];
     return path;
 }
 
@@ -5357,10 +5431,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     return index;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action {
+    if ((self = [super initWithFrame:frame]) != nil) {
         database_ = database;
-        title_ = [title retain];
+
+        target_ = target;
+        action_ = action;
 
         index_ = [[NSMutableArray alloc] initWithCapacity:32];
         indices_ = [[NSMutableDictionary alloc] initWithCapacity:32];
@@ -5369,13 +5445,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
         list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain];
-        [list_ setDataSource:self];
-        [list_ setDelegate:self];
-
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [list_ setRowHeight:73.0f];
         [self addSubview:list_];
 
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [list_ setDataSource:self];
+        [list_ setDelegate:self];
     } return self;
 }
 
@@ -5436,14 +5511,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _end
 }
 
-- (NSString *) title {
-    return title_;
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
-}
-
 - (void) resetCursor {
     [list_ scrollRectToVisible:CGRectMake(0, 0, 0, 0) animated:NO];
 }
@@ -5466,8 +5533,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) setObject:(id)object;
+- (void) setObject:(id)object forFilter:(SEL)filter;
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
+- (id) initWithFrame:(CGRect)frame database:(Database *)database target:(id)target action:(SEL)action filter:(SEL)filter with:(id)object;
 
 @end
 
@@ -5479,6 +5547,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
+- (void) setFilter:(SEL)filter {
+    filter_ = filter;
+
+    /* XXX: this is an unsafe optimization of doomy hell */
+    Method method(class_getInstanceMethod([Package class], filter));
+    _assert(method != NULL);
+    imp_ = method_getImplementation(method);
+    _assert(imp_ != NULL);
+}
+
 - (void) setObject:(id)object {
     if (object_ != nil)
         [object_ release];
@@ -5488,43 +5566,108 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         object_ = [object retain];
 }
 
+- (void) setObject:(id)object forFilter:(SEL)filter {
+    [self setFilter:filter];
+    [self setObject:object];
+}
+
 - (bool) hasPackage:(Package *)package {
     _profile(FilteredPackageTable$hasPackage)
         return [package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(imp_))(package, filter_, object_);
     _end
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
-    if ((self = [super initWithBook:book database:database title:title]) != nil) {
-        filter_ = filter;
-        object_ = object == nil ? nil : [object retain];
+- (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) {
+        [self setFilter:filter];
+        object_ = [object retain];
+        [self reloadData];
+    } return self;
+}
 
-        /* XXX: this is an unsafe optimization of doomy hell */
-        Method method(class_getInstanceMethod([Package class], filter));
-        _assert(method != NULL);
-        imp_ = method_getImplementation(method);
-        _assert(imp_ != NULL);
+@end
+/* }}} */
 
-        [self reloadData];
+/* Filtered Package Controller {{{ */
+@interface FilteredPackageController : CYViewController {
+    _transient Database *database_;
+    FilteredPackageTable *packages_;
+    NSString *title_;
+}
+
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
+
+@end
+
+@implementation FilteredPackageController
+
+- (void) dealloc {
+    [packages_ release];
+    [title_ release];
+
+    [super dealloc];
+}
+
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [packages_ deselectWithAnimation:animated];
+}
+
+- (void) didSelectPackage:(Package *)package {
+    PackageController *view([delegate_ packageController]);
+    [view setPackage:package];
+    [view setDelegate:delegate_];
+    [[self navigationController] pushViewController:view animated:YES];
+}
+
+- (NSString *) title { return title_; }
+
+- (id) initWithDatabase:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+        title_ = [title copy];
+        [[self navigationItem] setTitle:title_];
+
+        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_];
     } return self;
 }
 
+- (void) reloadData {
+    [packages_ reloadData];
+}
+
+- (void) setDelegate:(id)delegate {
+    [super setDelegate:delegate];
+    [packages_ setDelegate:delegate];
+}
+
 @end
+
 /* }}} */
 
-/* Add Source View {{{ */
-@interface AddSourceView : RVPage {
+/* Add Source Controller {{{ */
+@interface AddSourceController : CYViewController {
     _transient Database *database_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 
 @end
 
-@implementation AddSourceView
+@implementation AddSourceController
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
         database_ = database;
     } return self;
 }
@@ -5532,44 +5675,80 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Source Cell {{{ */
-@interface SourceCell : UITableCell {
+@interface SourceCell : UITableViewCell <
+    ContentDelegate
+> {
     UIImage *icon_;
     NSString *origin_;
     NSString *description_;
     NSString *label_;
+    ContentView *content_;
 }
 
-- (void) dealloc;
-
-- (SourceCell *) initWithSource:(Source *)source;
+- (void) setSource:(Source *)source;
 
 @end
 
 @implementation SourceCell
 
-- (void) dealloc {
+- (void) clearSource {
     [icon_ release];
     [origin_ release];
     [description_ release];
     [label_ release];
+
+    icon_ = nil;
+    origin_ = nil;
+    description_ = nil;
+    label_ = nil;
+}
+
+- (void) setSource:(Source *)source {
+    [self clearSource];
+
+    if (icon_ == nil)
+        icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
+    if (icon_ == nil)
+        icon_ = [UIImage applicationImageNamed:@"unknown.png"];
+    icon_ = [icon_ retain];
+
+    origin_ = [[source name] retain];
+    label_ = [[source uri] retain];
+    description_ = [[source description] retain];
+
+    [content_ setNeedsDisplay];
+}
+
+- (void) dealloc {
+    [self clearSource];
+    [content_ release];
     [super dealloc];
 }
 
-- (SourceCell *) initWithSource:(Source *)source {
-    if ((self = [super init]) != nil) {
-        if (icon_ == nil)
-            icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
-        if (icon_ == nil)
-            icon_ = [UIImage applicationImageNamed:@"unknown.png"];
-        icon_ = [icon_ retain];
-
-        origin_ = [[source name] retain];
-        label_ = [[source uri] retain];
-        description_ = [[source description] retain];
+- (SourceCell *) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
+    if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
+        UIView *content([self contentView]);
+        CGRect bounds([content bounds]);
+
+        content_ = [[ContentView alloc] initWithFrame:bounds];
+        [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [content_ setBackgroundColor:[UIColor whiteColor]];
+        [content addSubview:content_];
+
+        [content_ setDelegate:self];
+        [content_ setOpaque:YES];
     } return self;
 }
 
-- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
+- (void) setSelected:(BOOL)selected animated:(BOOL)animated {
+    [super setSelected:selected animated:animated];
+    [content_ setNeedsDisplay];
+}
+
+- (void) drawContentRect:(CGRect)rect {
+    bool selected([self isSelected]);
+    float width(rect.size.width);
+
     if (icon_ != nil)
         [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
 
@@ -5578,27 +5757,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     if (!selected)
         UISetColor(Black_);
-    [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
+    [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - 80) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
 
     if (!selected)
         UISetColor(Blue_);
-    [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
+    [label_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
 
     if (!selected)
         UISetColor(Gray_);
-    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
-
-    [super drawContentInRect:rect selected:selected];
+    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 40) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation];
 }
 
 @end
 /* }}} */
 /* Source Table {{{ */
-@interface SourceTable : RVPage {
+@interface SourceTable : CYViewController <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
     _transient Database *database_;
-    UISectionList *list_;
+    UITableView *list_;
     NSMutableArray *sources_;
-    UIActionSheet *alert_;
     int offset_;
 
     NSString *href_;
@@ -5614,7 +5793,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     BOOL cydia_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
+
+- (void) updateButtonsForEditingStatus:(BOOL)editing animated:(BOOL)animated;
 
 @end
 
@@ -5629,9 +5810,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) dealloc {
-    [[list_ table] setDelegate:nil];
-    [list_ setDataSource:nil];
-
     if (href_ != nil)
         [href_ release];
     if (hud_ != nil)
@@ -5650,11 +5828,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (int) numberOfSectionsInSectionList:(UISectionList *)list {
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
+}
+
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
     return offset_ == 0 ? 1 : 2;
 }
 
-- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
+- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
     switch (section + (offset_ == 0 ? 1 : 0)) {
         case 0: return UCLocalize("ENTERED_BY_USER");
         case 1: return UCLocalize("INSTALLED_BY_PACKAGE");
@@ -5663,49 +5846,51 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
-    switch (section + (offset_ == 0 ? 1 : 0)) {
-        case 0: return 0;
-        case 1: return offset_;
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    int count = [sources_ count];
+    switch (section) {
+        case 0: return (offset_ == 0 ? count : offset_);
+        case 1: return count - offset_;
 
         _nodefault
     }
 }
 
-- (int) numberOfRowsInTable:(UITable *)table {
-    return [sources_ count];
+- (Source *) sourceAtIndexPath:(NSIndexPath *)indexPath {
+    unsigned idx = 0;
+    switch (indexPath.section) {
+        case 0: idx = indexPath.row; break;
+        case 1: idx = indexPath.row + offset_; break;
+
+        _nodefault
+    }
+    return [sources_ objectAtIndex:idx];
 }
 
-- (float) table:(UITable *)table heightForRow:(int)row {
-    Source *source = [sources_ objectAtIndex:row];
+- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+    Source *source = [self sourceAtIndexPath:indexPath];
     return [source description] == nil ? 56 : 73;
 }
 
-- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
-    Source *source = [sources_ objectAtIndex:row];
-    // XXX: weird warning, stupid selectors ;P
-    return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
-}
+- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    static NSString *cellIdentifier = @"SourceCell";
 
-- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
-    return YES;
-}
+    SourceCell *cell = (SourceCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
+    if(cell == nil) cell = [[[SourceCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellIdentifier] autorelease];
+    [cell setSource:[self sourceAtIndexPath:indexPath]];
 
-- (BOOL) table:(UITable *)table canSelectRow:(int)row {
-    return YES;
+    return cell;
 }
 
-- (void) tableRowSelected:(NSNotification*)notification {
-    UITable *table([list_ table]);
-    int row([table selectedRow]);
-    if (row == INT_MAX)
-        return;
+- (UITableViewCellAccessoryType) tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
+    return UITableViewCellAccessoryDisclosureIndicator;
+}
 
-    Source *source = [sources_ objectAtIndex:row];
+- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    Source *source = [self sourceAtIndexPath:indexPath];
 
-    PackageTable *packages = [[[FilteredPackageTable alloc]
-        initWithBook:book_
-        database:database_
+    FilteredPackageController *packages = [[[FilteredPackageController alloc]
+        initWithDatabase:database_
         title:[source label]
         filter:@selector(isVisibleInSource:)
         with:source
@@ -5713,20 +5898,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [packages setDelegate:delegate_];
 
-    [book_ pushPage:packages];
+    [[self navigationController] pushViewController:packages animated:YES];
 }
 
-- (BOOL) table:(UITable *)table canDeleteRow:(int)row {
-    Source *source = [sources_ objectAtIndex:row];
+- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
+    Source *source = [self sourceAtIndexPath:indexPath];
     return [source record] != nil;
 }
 
-- (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
-    [[list_ table] setDeleteConfirmationRow:row];
-}
-
-- (void) table:(UITable *)table deleteRow:(int)row {
-    Source *source = [sources_ objectAtIndex:row];
+- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
+    Source *source = [self sourceAtIndexPath:indexPath];
     [Sources_ removeObjectForKey:[source key]];
     [delegate_ syncData];
 }
@@ -5783,42 +5964,41 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
                 defer = true;
 
-                UIActionSheet *sheet = [[[UIActionSheet alloc]
+                UIAlertView *alert = [[[UIAlertView alloc]
                     initWithTitle:UCLocalize("SOURCE_WARNING")
-                    buttons:[NSArray arrayWithObjects:UCLocalize("ADD_ANYWAY"), UCLocalize("CANCEL"), nil]
-                    defaultButtonIndex:0
+                    message:warning
                     delegate:self
-                    context:@"warning"
+                    cancelButtonTitle:UCLocalize("CANCEL")
+                    otherButtonTitles:UCLocalize("ADD_ANYWAY"), nil
                 ] autorelease];
 
-                [sheet setNumberOfRows:1];
-
-                [sheet setBodyText:warning];
-                [sheet popupAlertAnimated:YES];
+                [alert setContext:@"warning"];
+                [alert setNumberOfRows:1];
+                [alert show];
             } else
                 [self complete];
         } else if (error_ != nil) {
-            UIActionSheet *sheet = [[[UIActionSheet alloc]
+            UIAlertView *alert = [[[UIAlertView alloc]
                 initWithTitle:UCLocalize("VERIFICATION_ERROR")
-                buttons:[NSArray arrayWithObjects:UCLocalize("OK"), nil]
-                defaultButtonIndex:0
+                message:[error_ localizedDescription]
                 delegate:self
-                context:@"urlerror"
+                cancelButtonTitle:UCLocalize("OK")
+                otherButtonTitles:nil
             ] autorelease];
 
-            [sheet setBodyText:[error_ localizedDescription]];
-            [sheet popupAlertAnimated:YES];
+            [alert setContext:@"urlerror"];
+            [alert show];
         } else {
-            UIActionSheet *sheet = [[[UIActionSheet alloc]
+            UIAlertView *alert = [[[UIAlertView alloc]
                 initWithTitle:UCLocalize("NOT_REPOSITORY")
-                buttons:[NSArray arrayWithObjects:UCLocalize("OK"), nil]
-                defaultButtonIndex:0
+                message:UCLocalize("NOT_REPOSITORY_EX")
                 delegate:self
-                context:@"trivial"
+                cancelButtonTitle:UCLocalize("OK")
+                otherButtonTitles:nil
             ] autorelease];
 
-            [sheet setBodyText:UCLocalize("NOT_REPOSITORY_EX")];
-            [sheet popupAlertAnimated:YES];
+            [alert setContext:@"trivial"];
+            [alert show];
         }
 
         [delegate_ setStatusBarShowsProgress:NO];
@@ -5857,11 +6037,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self _endConnection:connection];
 }
 
+- (NSString *) title { return UCLocalize("SOURCES"); }
+
 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
     NSMutableURLRequest *request = [NSMutableURLRequest
         requestWithURL:[NSURL URLWithString:href]
         cachePolicy:NSURLRequestUseProtocolCachePolicy
-        timeoutInterval:20.0
+        timeoutInterval:120.0
     ];
 
     [request setHTTPMethod:method];
@@ -5870,20 +6052,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
     if (UniqueID_ != nil)
         [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
-
     if (Role_ != nil)
         [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
 
     return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
+- (void)alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+    NSString *context([alert context]);
 
     if ([context isEqualToString:@"source"]) {
         switch (button) {
             case 1: {
-                NSString *href = [[sheet textField] text];
+                NSString *href = [[alert textField] text];
 
                 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
 
@@ -5904,24 +6085,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 [hud_ setText:UCLocalize("VERIFYING_URL")];
             } break;
 
-            case 2:
+            case 0:
             break;
 
             _nodefault
         }
 
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     } else if ([context isEqualToString:@"trivial"])
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     else if ([context isEqualToString:@"urlerror"])
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     else if ([context isEqualToString:@"warning"]) {
         switch (button) {
             case 1:
                 [self complete];
             break;
 
-            case 2:
+            case 0:
             break;
 
             _nodefault
@@ -5930,37 +6111,26 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [href_ release];
         href_ = nil;
 
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     }
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
+        [[self navigationItem] setTitle:UCLocalize("SOURCES")];
+        [self updateButtonsForEditingStatus:NO animated:NO];
+
         database_ = database;
         sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
-        //list_ = [[UITable alloc] initWithFrame:[self bounds]];
-        list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
-        [list_ setShouldHideHeaderInShortLists:NO];
+        list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [[self view] addSubview:list_];
 
-        [self addSubview:list_];
         [list_ setDataSource:self];
-
-        UITableColumn *column = [[UITableColumn alloc]
-            initWithTitle:UCLocalize("NAME")
-            identifier:@"name"
-            width:[self frame].size.width
-        ];
-
-        UITable *table = [list_ table];
-        [table setSeparatorStyle:1];
-        [table addTableColumn:column];
-        [table setDelegate:self];
+        [list_ setDelegate:self];
 
         [self reloadData];
-
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
     } return self;
 }
 
@@ -5975,140 +6145,162 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
     _trace();
 
-    int count([sources_ count]);
-    for (offset_ = 0; offset_ != count; ++offset_) {
-        Source *source = [sources_ objectAtIndex:offset_];
-        if ([source record] == nil)
-            break;
+     int count([sources_ count]);
+    offset_ = 0;
+    for (int i = 0; i != count; i++) {
+        if ([[sources_ objectAtIndex:i] record] == nil) break;
+        else offset_++;
     }
 
+    [list_ setEditing:NO];
+    [self updateButtonsForEditingStatus:NO animated:NO];
     [list_ reloadData];
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
-}
-
-- (void) _leftButtonClicked {
-    /*[book_ pushPage:[[[AddSourceView alloc]
+- (void) addButtonClicked {
+    /*[book_ pushPage:[[[AddSourceController alloc]
         initWithBook:book_
         database:database_
     ] autorelease]];*/
 
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
+    UIAlertView *alert = [[[UIAlertView alloc]
         initWithTitle:UCLocalize("ENTER_APT_URL")
-        buttons:[NSArray arrayWithObjects:UCLocalize("ADD_SOURCE"), UCLocalize("CANCEL"), nil]
-        defaultButtonIndex:0
+        message:nil
         delegate:self
-        context:@"source"
+        cancelButtonTitle:UCLocalize("CANCEL")
+        otherButtonTitles:UCLocalize("ADD_SOURCE"), nil
     ] autorelease];
 
-    [sheet setNumberOfRows:1];
+    [alert setContext:@"source"];
+    [alert setTransform:CGAffineTransformTranslate([alert transform], 0.0, 100.0)];
 
-    [sheet addTextFieldWithValue:@"http://" label:@""];
+    [alert setNumberOfRows:1];
+    [alert addTextFieldWithValue:@"http://" label:@""];
 
-    UITextInputTraits *traits = [[sheet textField] textInputTraits];
+    UITextInputTraits *traits = [[alert textField] textInputTraits];
     [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
     [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
     [traits setKeyboardType:UIKeyboardTypeURL];
     // XXX: UIReturnKeyDone
     [traits setReturnKeyType:UIReturnKeyNext];
 
-    [sheet popupAlertAnimated:YES];
-}
-
-- (void) _rightButtonClicked {
-    UITable *table = [list_ table];
-    BOOL editing = [table isRowDeletionEnabled];
-    [table enableRowDeletion:!editing animated:YES];
-    [book_ reloadButtonsForPage:self];
+    [alert show];
 }
 
-- (NSString *) title {
-    return UCLocalize("SOURCES");
+- (void) updateButtonsForEditingStatus:(BOOL)editing animated:(BOOL)animated {
+    UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]
+        initWithTitle:UCLocalize("ADD")
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(addButtonClicked)
+    ];
+    [[self navigationItem] setLeftBarButtonItem:editing ? leftItem : [[self navigationItem] backBarButtonItem] animated:animated];
+    [leftItem release];
+
+    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
+        initWithTitle:editing ? UCLocalize("DONE") : UCLocalize("EDIT")
+        style:editing ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain
+        target:self
+        action:@selector(editButtonClicked)
+    ];
+    [[self navigationItem] setRightBarButtonItem:rightItem animated:animated];
+    [rightItem release];
+
+    if (IsWildcat_ && !editing) {
+        UIBarButtonItem *settingsItem = [[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("SETTINGS")
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(settingsButtonClicked)
+            ];
+        [[self navigationItem] setLeftBarButtonItem:settingsItem];
+        [settingsItem release];
+    }
 }
 
-- (NSString *) leftButtonTitle {
-    return [[list_ table] isRowDeletionEnabled] ? UCLocalize("ADD") : nil;
+- (void) settingsButtonClicked {
+    [delegate_ showSettings];
 }
 
-- (id) rightButtonTitle {
-    return [[list_ table] isRowDeletionEnabled] ? UCLocalize("DONE") : UCLocalize("EDIT");
-}
+- (void) editButtonClicked {
+    [list_ setEditing:![list_ isEditing] animated:YES];
 
-- (UINavigationButtonStyle) rightButtonStyle {
-    return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+    [self updateButtonsForEditingStatus:[list_ isEditing] animated:YES];
 }
 
 @end
 /* }}} */
 
-/* Installed View {{{ */
-@interface InstalledView : RVPage {
-    _transient Database *database_;
-    FilteredPackageTable *packages_;
+/* Installed Controller {{{ */
+@interface InstalledController : FilteredPackageController {
     BOOL expert_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
+
+- (void) updateRoleButton;
+- (void) queueStatusDidChange;
 
 @end
 
-@implementation InstalledView
+@implementation InstalledController
 
 - (void) dealloc {
-    [packages_ release];
     [super dealloc];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
-        database_ = database;
-
-        packages_ = [[FilteredPackageTable alloc]
-            initWithBook:book
-            database:database
-            title:nil
-            filter:@selector(isInstalledAndVisible:)
-            with:[NSNumber numberWithBool:YES]
-        ];
-
-        [self addSubview:packages_];
+- (NSString *) title { return UCLocalize("INSTALLED"); }
 
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") filter:@selector(isInstalledAndVisible:) with:[NSNumber numberWithBool:YES]]) != nil) {
+        [self updateRoleButton];
+        [self queueStatusDidChange];
     } return self;
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [packages_ resetViewAnimated:animated];
+#if !AlwaysReload
+- (void) queueButtonClicked {
+    [delegate_ queue];
 }
+#endif
 
-- (void) reloadData {
-    [packages_ reloadData];
+- (void) queueStatusDidChange {
+#if !AlwaysReload
+    if (IsWildcat_) {
+        UIBarButtonItem *queueItem = [[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("QUEUE")
+            style:UIBarButtonItemStyleDone
+            target:self
+            action:@selector(queueButtonClicked)
+        ];
+        if (Queuing_) [[self navigationItem] setLeftBarButtonItem:queueItem];
+        else [[self navigationItem] setLeftBarButtonItem:nil];
+        [queueItem release];
+    }
+#endif
 }
 
-- (void) _rightButtonClicked {
-    [packages_ setObject:[NSNumber numberWithBool:expert_]];
+- (void) reloadData {
     [packages_ reloadData];
-    expert_ = !expert_;
-    [book_ reloadButtonsForPage:self];
-}
-
-- (NSString *) title {
-    return UCLocalize("INSTALLED");
 }
 
-- (NSString *) backButtonTitle {
-    return UCLocalize("PACKAGES");
+- (void) updateRoleButton {
+    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
+        initWithTitle:expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE")
+        style:expert_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain
+        target:self
+        action:@selector(roleButtonClicked)
+    ];
+    if (Role_ != nil && ![Role_ isEqualToString:@"Developer"]) [[self navigationItem] setRightBarButtonItem:rightItem];
+    [rightItem release];
 }
 
-- (id) rightButtonTitle {
-    return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE");
-}
+- (void) roleButtonClicked {
+    [packages_ setObject:[NSNumber numberWithBool:expert_]];
+    [packages_ reloadData];
+    expert_ = !expert_;
 
-- (UINavigationButtonStyle) rightButtonStyle {
-    return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+    [self updateRoleButton];
 }
 
 - (void) setDelegate:(id)delegate {
@@ -6119,359 +6311,314 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Home View {{{ */
-@interface HomeView : CydiaBrowserView {
+/* Home Controller {{{ */
+@interface HomeController : CYBrowserController {
 }
 
 @end
 
-@implementation HomeView
-
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
-
-    if ([context isEqualToString:@"about"])
-        [sheet dismiss];
-    else
-        [super alertSheet:sheet buttonClicked:button];
-}
+@implementation HomeController
 
 - (void) _setMoreHeaders:(NSMutableURLRequest *)request {
     [super _setMoreHeaders:request];
     if (ChipID_ != nil)
         [request setValue:ChipID_ forHTTPHeaderField:@"X-Chip-ID"];
+    if (UniqueID_ != nil)
+        [request setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
 }
 
-- (void) _leftButtonClicked {
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:UCLocalize("ABOUT_CYDIA")
-        buttons:[NSArray arrayWithObjects:UCLocalize("CLOSE"), nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:@"about"
-    ] autorelease];
+- (void) aboutButtonClicked {
+    UIAlertView *alert = [[[UIAlertView alloc] init] autorelease];
+    [alert setTitle:UCLocalize("ABOUT_CYDIA")];
+    [alert addButtonWithTitle:UCLocalize("CLOSE")];
+    [alert setCancelButtonIndex:0];
 
-    [sheet setBodyText:
-        @"Copyright (C) 2008-2009\n"
+    [alert setMessage:
+        @"Copyright (C) 2008-2010\n"
         "Jay Freeman (saurik)\n"
         "saurik@saurik.com\n"
-        "http://www.saurik.com/\n"
-        "\n"
-        "The Okori Group\n"
-        "http://www.theokorigroup.com/\n"
-        "\n"
-        "College of Creative Studies,\n"
-        "University of California,\n"
-        "Santa Barbara\n"
-        "http://www.ccs.ucsb.edu/"
+        "http://www.saurik.com/"
     ];
 
-    [sheet popupAlertAnimated:YES];
+    [alert show];
+}
+
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    [[self navigationController] setNavigationBarHidden:YES animated:animated];
+}
+
+- (void) viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    [[self navigationController] setNavigationBarHidden:NO animated:animated];
 }
 
-- (NSString *) leftButtonTitle {
-    return UCLocalize("ABOUT");
+- (id) init {
+    if ((self = [super init]) != nil) {
+        UIBarButtonItem *aboutItem = [[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("ABOUT")
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(aboutButtonClicked)
+        ];
+        [[self navigationItem] setLeftBarButtonItem:aboutItem];
+        [aboutItem release];
+    } return self;
 }
 
 @end
 /* }}} */
-/* Manage View {{{ */
-@interface ManageView : CydiaBrowserView {
+/* Manage Controller {{{ */
+@interface ManageController : CYBrowserController {
 }
 
+- (void) queueStatusDidChange;
 @end
 
-@implementation ManageView
+@implementation ManageController
 
-- (NSString *) title {
-    return UCLocalize("MANAGE");
-}
+- (id) init {
+    if ((self = [super init]) != nil) {
+        [[self navigationItem] setTitle:UCLocalize("MANAGE")];
+
+        UIBarButtonItem *settingsItem = [[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("SETTINGS")
+            style:UIBarButtonItemStylePlain
+            target:self
+            action:@selector(settingsButtonClicked)
+        ];
+        [[self navigationItem] setLeftBarButtonItem:settingsItem];
+        [settingsItem release];
 
-- (void) _leftButtonClicked {
-    [delegate_ askForSettings];
-    [delegate_ updateData];
+        [self queueStatusDidChange];
+    } return self;
 }
 
-- (NSString *) leftButtonTitle {
-    return UCLocalize("SETTINGS");
+- (void) settingsButtonClicked {
+    [delegate_ showSettings];
 }
 
 #if !AlwaysReload
-- (id) _rightButtonTitle {
-    return Queuing_ ? UCLocalize("QUEUE") : nil;
+- (void) queueButtonClicked {
+    [delegate_ queue];
 }
 
-- (UINavigationButtonStyle) rightButtonStyle {
-    return Queuing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+- (void) applyLoadingTitle {
+    // No "Loading" title.
 }
 
-- (void) _rightButtonClicked {
-    [delegate_ queue];
+- (void) applyRightButton {
+    // No right button.
 }
 #endif
 
-- (bool) isLoading {
-    return false;
-}
+- (void) queueStatusDidChange {
+#if !AlwaysReload
+    if (!IsWildcat_ && Queuing_) {
+        UIBarButtonItem *queueItem = [[UIBarButtonItem alloc]
+            initWithTitle:UCLocalize("QUEUE")
+            style:UIBarButtonItemStyleDone
+            target:self
+            action:@selector(queueButtonClicked)
+        ];
+        [[self navigationItem] setRightBarButtonItem:queueItem];
+
+        [queueItem release];
+    } else {
+        [[self navigationItem] setRightBarButtonItem:nil];
+    }
+#endif
+}
+
+- (bool) isLoading {
+    return false;
+}
 
 @end
 /* }}} */
 
-/* Cydia Book {{{ */
-@interface CYBook : RVBook <
-    ProgressDelegate
-> {
-    _transient Database *database_;
-    UINavigationBar *overlay_;
-    UINavigationBar *underlay_;
+/* Refresh Bar {{{ */
+@interface RefreshBar : UINavigationBar {
     UIProgressIndicator *indicator_;
     UITextLabel *prompt_;
     UIProgressBar *progress_;
     UINavigationButton *cancel_;
-    bool updating_;
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database;
-- (void) update;
-- (BOOL) updating;
-
 @end
 
-@implementation CYBook
-
-- (void) dealloc {
-    [overlay_ release];
-    [indicator_ release];
-    [prompt_ release];
-    [progress_ release];
-    [cancel_ release];
-    [super dealloc];
-}
-
-- (NSString *) getTitleForPage:(RVPage *)page {
-    return [super getTitleForPage:page];
-}
-
-- (BOOL) updating {
-    return updating_;
-}
-
-- (void) update {
-    [UIView beginAnimations:nil context:NULL];
-
-    CGRect ovrframe = [overlay_ frame];
-    ovrframe.origin.y = 0;
-    [overlay_ setFrame:ovrframe];
+@implementation RefreshBar
 
-    CGRect barframe = [navbar_ frame];
-    barframe.origin.y += ovrframe.size.height;
-    [navbar_ setFrame:barframe];
+- (void) positionViews {
+    CGRect frame = [cancel_ frame];
+    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];
 
-    CGRect trnframe = [transition_ frame];
-    trnframe.origin.y += ovrframe.size.height;
-    trnframe.size.height -= ovrframe.size.height;
-    [transition_ setFrame:trnframe];
+    CGSize prgsize = {75, 100};
+    CGRect prgrect = {{
+        [self frame].size.width - prgsize.width - 10,
+        ([self frame].size.height - prgsize.height) / 2
+    } , prgsize};
+    [progress_ setFrame:prgrect];
 
-    [UIView endAnimations];
+    CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]);
+    unsigned indoffset = ([self frame].size.height - indsize.height) / 2;
+    CGRect indrect = {{indoffset, indoffset}, indsize};
+    [indicator_ setFrame:indrect];
 
-    [indicator_ startAnimation];
-    [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
-    [progress_ setProgress:0];
-
-    updating_ = true;
-    [overlay_ addSubview:cancel_];
-
-    [NSThread
-        detachNewThreadSelector:@selector(_update)
-        toTarget:self
-        withObject:nil
-    ];
+    CGSize prmsize = {215, indsize.height + 4};
+    CGRect prmrect = {{
+        indoffset * 2 + indsize.width,
+        unsigned([self frame].size.height - prmsize.height) / 2 - 1
+    }, prmsize};
+    [prompt_ setFrame:prmrect];
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
+- (void)setFrame:(CGRect)frame {
+    [super setFrame:frame];
 
-    if ([context isEqualToString:@"refresh"])
-        [sheet dismiss];
+    [self positionViews];
 }
 
-- (void) _update_ {
-    updating_ = false;
-
-    [indicator_ stopAnimation];
-
-    [UIView beginAnimations:nil context:NULL];
-
-    CGRect ovrframe = [overlay_ frame];
-    ovrframe.origin.y = -ovrframe.size.height;
-    [overlay_ setFrame:ovrframe];
-
-    CGRect barframe = [navbar_ frame];
-    barframe.origin.y -= ovrframe.size.height;
-    [navbar_ setFrame:barframe];
+- (id) initWithFrame:(CGRect)frame delegate:(id)delegate {
+    if ((self = [super initWithFrame:frame])) {
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
 
-    CGRect trnframe = [transition_ frame];
-    trnframe.origin.y -= ovrframe.size.height;
-    trnframe.size.height += ovrframe.size.height;
-    [transition_ setFrame:trnframe];
+        [self setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
+        [self setBarStyle:UIBarStyleBlack];
 
-    [UIView commitAnimations];
-
-    [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
-}
-
-- (id) initWithFrame:(CGRect)frame database:(Database *)database {
-    if ((self = [super initWithFrame:frame]) != nil) {
-        database_ = database;
-
-        CGRect ovrrect([navbar_ bounds]);
-        ovrrect.size.height = [UINavigationBar defaultSize].height;
-        ovrrect.origin.y = -ovrrect.size.height;
-
-        overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
-        [self addSubview:overlay_];
-
-        ovrrect.origin.y = frame.size.height;
-        underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
-        [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
-        [self addSubview:underlay_];
-
-        [overlay_ setBarStyle:1];
-        [underlay_ setBarStyle:1];
-
-        int barstyle([overlay_ _barStyle:NO]);
-        bool ugly(barstyle == 0);
+        UIBarStyle barstyle([self _barStyle:NO]);
+        bool ugly(barstyle == UIBarStyleDefault);
 
         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_ = [[UIProgressIndicator alloc] initWithFrame:CGRectZero];
         [indicator_ setStyle:style];
-        [overlay_ addSubview:indicator_];
-
-        CGSize prmsize = {215, indsize.height + 4};
-
-        CGRect prmrect = {{
-            indoffset * 2 + indsize.width,
-            unsigned(ovrrect.size.height - prmsize.height) / 2 - 1
-        }, prmsize};
-
-        UIFont *font([UIFont systemFontOfSize:15]);
-
-        prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
+        [indicator_ startAnimation];
+        [self addSubview:indicator_];
 
+        prompt_ = [[UITextLabel alloc] initWithFrame:CGRectZero];
         [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};
+        [prompt_ setFont:[UIFont systemFontOfSize:15]];
+        [self addSubview:prompt_];
 
-        progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
+        progress_ = [[UIProgressBar alloc] initWithFrame:CGRectZero];
+        [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin];
         [progress_ setStyle:0];
-        [overlay_ addSubview:progress_];
+        [self addSubview:progress_];
 
         cancel_ = [[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted];
-        [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
-
-        CGRect frame = [cancel_ frame];
-        frame.origin.x = ovrrect.size.width - frame.size.width - 5;
-        frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
-        [cancel_ setFrame:frame];
-
+        [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+        [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside];
         [cancel_ setBarStyle:barstyle];
+
+        [self positionViews];
     } return self;
 }
 
-- (void) _onCancel {
-    updating_ = false;
+- (void) cancel {
     [cancel_ removeFromSuperview];
 }
 
-- (void) _update { _pooled
-    Status status;
-    status.setDelegate(self);
-    [database_ updateWithStatus:status];
+- (void) start {
+    [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
+    [progress_ setProgress:0];
+    [self addSubview:cancel_];
+}
 
-    [self
-        performSelectorOnMainThread:@selector(_update_)
-        withObject:nil
-        waitUntilDone:NO
-    ];
+- (void) stop {
+    [cancel_ removeFromSuperview];
 }
 
-- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
-    [prompt_ setText:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]];
+- (void) setPrompt:(NSString *)prompt {
+    [prompt_ setText:prompt];
 }
 
-/*
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), UCLocalize("REFRESH")]
-        buttons:[NSArray arrayWithObjects:
-            UCLocalize("OK"),
-        nil]
-        defaultButtonIndex:0
-        delegate:self
-        context:@"refresh"
-    ] autorelease];
+- (void) setProgress:(float)progress {
+    [progress_ setProgress:progress];
+}
 
-    [sheet setBodyText:error];
-    [sheet popupAlertAnimated:YES];
+@end
+/* }}} */
 
-    [self reloadButtons];
-*/
+@class CYNavigationController;
 
-- (void) setProgressTitle:(NSString *)title {
-    [self
-        performSelectorOnMainThread:@selector(_setProgressTitle:)
-        withObject:title
-        waitUntilDone:YES
-    ];
+/* Cydia Tab Bar Controller {{{ */
+@interface CYTabBarController : UITabBarController {
+    Database *database_;
 }
 
-- (void) setProgressPercent:(float)percent {
-    [self
-        performSelectorOnMainThread:@selector(_setProgressPercent:)
-        withObject:[NSNumber numberWithFloat:percent]
-        waitUntilDone:YES
-    ];
+@end
+
+@implementation CYTabBarController
+
+/* XXX: some logic should probably go here related to
+freeing the view controllers on tab change */
+
+- (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];
+    }
 }
 
-- (void) startProgress {
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+    } return self;
 }
 
-- (void) addProgressOutput:(NSString *)output {
-    [self
-        performSelectorOnMainThread:@selector(_addProgressOutput:)
-        withObject:output
-        waitUntilDone:YES
-    ];
+@end
+/* }}} */
+
+/* Cydia Navigation Controller {{{ */
+@interface CYNavigationController : UINavigationController {
+    _transient Database *database_;
+    id<UINavigationControllerDelegate> delegate_;
 }
 
-- (bool) isCancelling:(size_t)received {
-    return !updating_;
+- (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) _setProgressTitle:(NSString *)title {
-    [prompt_ setText:title];
+- (void) dealloc {
+    [super dealloc];
 }
 
-- (void) _setProgressPercent:(NSNumber *)percent {
-    [progress_ setProgress:[percent floatValue]];
+- (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) _addProgressOutput:(NSString *)output {
+- (void) setDelegate:(id<UINavigationControllerDelegate>)delegate {
+    delegate_ = delegate;
+}
+
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+    } return self;
 }
 
 @end
@@ -6577,24 +6724,28 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Sections View {{{ */
-@interface SectionsView : RVPage {
+/* Sections Controller {{{ */
+@interface SectionsController : CYViewController <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
     _transient Database *database_;
     NSMutableArray *sections_;
     NSMutableArray *filtered_;
-    UITransitionView *transition_;
-    UITable *list_;
+    UITableView *list_;
     UIView *accessory_;
     BOOL editing_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 - (void) reloadData;
 - (void) resetView;
 
+- (void) editButtonClicked;
+
 @end
 
-@implementation SectionsView
+@implementation SectionsController
 
 - (void) dealloc {
     [list_ setDataSource:nil];
@@ -6602,55 +6753,49 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [sections_ release];
     [filtered_ release];
-    [transition_ release];
     [list_ release];
     [accessory_ release];
     [super dealloc];
 }
 
-- (int) numberOfRowsInTable:(UITable *)table {
-    return editing_ ? [sections_ count] : [filtered_ count] + 1;
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
 }
 
-- (float) table:(UITable *)table heightForRow:(int)row {
-    return 45;
+- (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath {
+    Section *section = (editing_ ? [sections_ objectAtIndex:[indexPath row]] : ([indexPath row] == 0 ? nil : [filtered_ objectAtIndex:([indexPath row] - 1)]));
+    return section;
 }
 
-- (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;
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    return editing_ ? [sections_ count] : [filtered_ count] + 1;
 }
 
-- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
-    return !editing_;
-}
+/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+    return 45.0f;
+}*/
 
-- (BOOL) table:(UITable *)table canSelectRow:(int)row {
-    return !editing_;
-}
+- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    static NSString *reuseIdentifier = @"SectionCell";
 
-- (void) tableRowSelected:(NSNotification *)notification {
-    int row = [[notification object] selectedRow];
-    if (row == INT_MAX)
-        return;
+    SectionCell *cell = (SectionCell *) [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+    if (cell == nil) cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
+    [cell setSection:[self sectionAtIndexPath:indexPath] editing:editing_];
 
-    Section *section;
-    NSString *name;
+    return cell;
+}
+
+- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+    Section *section = [self sectionAtIndexPath:indexPath];
+    NSString *name = [section name];
     NSString *title;
 
-    if (row == 0) {
+    if ([indexPath row] == 0) {
         section = nil;
         name = nil;
         title = UCLocalize("ALL_PACKAGES");
     } else {
-        section = [filtered_ objectAtIndex:(row - 1)];
-        name = [section name];
-
         if (name != nil) {
             name = [NSString stringWithString:name];
             title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
@@ -6660,9 +6805,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         }
     }
 
-    PackageTable *table = [[[FilteredPackageTable alloc]
-        initWithBook:book_
-        database:database_
+    FilteredPackageController *table = [[[FilteredPackageController alloc]
+        initWithDatabase:database_
         title:title
         filter:@selector(isVisibleInSection:)
         with:name
@@ -6670,38 +6814,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [table setDelegate:delegate_];
 
-    [book_ pushPage:table];
+    [[self navigationController] pushViewController:table animated:YES];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (NSString *) title { return UCLocalize("SECTIONS"); }
+
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
         database_ = database;
 
+        [[self navigationItem] setTitle:UCLocalize("SECTIONS")];
+
         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:UCLocalize("NAME")
-            identifier:@"name"
-            width:[self frame].size.width
-        ] autorelease];
+        list_ = [[UITableView alloc] initWithFrame:[[self view] bounds]];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [list_ setRowHeight:45.0f];
+        [[self view] addSubview:list_];
 
         [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;
 }
 
@@ -6788,42 +6923,33 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [filtered_ addObject:section];
     }
 
+    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
+        initWithTitle:[sections_ count] == 0 ? nil : UCLocalize("EDIT")
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(editButtonClicked)
+    ];
+    [[self navigationItem] setRightBarButtonItem:rightItem animated:[[self navigationItem] rightBarButtonItem] != nil];
+    [rightItem release];
+
     [list_ reloadData];
     _trace();
 }
 
 - (void) resetView {
     if (editing_)
-        [self _rightButtonClicked];
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
+        [self editButtonClicked];
 }
 
-- (void) _rightButtonClicked {
+- (void) editButtonClicked {
     if ((editing_ = !editing_))
         [list_ reloadData];
     else
         [delegate_ updateData];
-    [book_ reloadTitleForPage:self];
-    [book_ reloadButtonsForPage:self];
-}
-
-- (NSString *) title {
-    return editing_ ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS");
-}
-
-- (NSString *) backButtonTitle {
-    return UCLocalize("SECTIONS");
-}
-
-- (id) rightButtonTitle {
-    return [sections_ count] == 0 ? nil : editing_ ? UCLocalize("DONE") : UCLocalize("EDIT");
-}
 
-- (UINavigationButtonStyle) rightButtonStyle {
-    return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+    [[self navigationItem] setTitle:editing_ ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS")];
+    [[[self navigationItem] rightBarButtonItem] setTitle:[sections_ count] == 0 ? nil : editing_ ? UCLocalize("DONE") : UCLocalize("EDIT")];
+    [[[self navigationItem] rightBarButtonItem] setStyle:editing_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain];
 }
 
 - (UIView *) accessoryView {
@@ -6832,21 +6958,25 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 /* }}} */
-/* Changes View {{{ */
-@interface ChangesView : RVPage {
+/* Changes Controller {{{ */
+@interface ChangesController : CYViewController <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
     _transient Database *database_;
     NSMutableArray *packages_;
     NSMutableArray *sections_;
     UITableView *list_;
     unsigned upgrades_;
+    BOOL hasSentFirstLoad_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate;
 - (void) reloadData;
 
 @end
 
-@implementation ChangesView
+@implementation ChangesController
 
 - (void) dealloc {
     [list_ setDelegate:nil];
@@ -6858,6 +6988,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    if (!hasSentFirstLoad_) {
+        hasSentFirstLoad_ = YES;
+        [self performSelector:@selector(reloadData) withObject:nil afterDelay:0.0];
+    } else {
+        [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
+    }
+}
+
 - (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
     NSInteger count([sections_ count]);
     return count == 0 ? 1 : count;
@@ -6882,64 +7022,58 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
-    PackageCell *cell([table dequeueReusableCellWithIdentifier:@"Package"]);
+    PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
     if (cell == nil)
         cell = [[[PackageCell alloc] init] autorelease];
     [cell setPackage:[self packageAtIndexPath:path]];
     return cell;
 }
 
-- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
-    return 73;
+/*- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
     return [PackageCell heightForPackage:[self packageAtIndexPath:path]];
-}
+}*/
 
 - (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
     Package *package([self packageAtIndexPath:path]);
-    PackageView *view([delegate_ packageView]);
+    PackageController *view([delegate_ packageController]);
     [view setDelegate:delegate_];
     [view setPackage:package];
-    [book_ pushPage:view];
+    [[self navigationController] pushViewController:view animated:YES];
     return path;
 }
 
-- (void) _leftButtonClicked {
-    [(CYBook *)book_ update];
-    [self reloadButtons];
+- (void) refreshButtonClicked {
+    [delegate_ beginUpdate];
+    [[self navigationItem] setLeftBarButtonItem:nil];
 }
 
-- (void) _rightButtonClicked {
+- (void) upgradeButtonClicked {
     [delegate_ distUpgrade];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (NSString *) title { return UCLocalize("CHANGES"); }
+
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
+    if ((self = [super init]) != nil) {
         database_ = database;
+        [[self navigationItem] setTitle:UCLocalize("CHANGES")];
 
         packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
         sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
-        list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain];
-        [self addSubview:list_];
+        list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [list_ setRowHeight:73.0f];
+        [[self view] addSubview:list_];
 
-        //XXX:[list_ setShouldHideHeaderInShortLists:NO];
         [list_ setDataSource:self];
         [list_ setDelegate:self];
-        //[list_ setSectionListStyle:1];
 
-        [self reloadData];
-
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        delegate_ = delegate;
     } return self;
 }
 
-- (void) reloadData {
-    NSArray *packages = [database_ packages];
-
-    [packages_ removeAllObjects];
-    [sections_ removeAllObjects];
-
+- (void) _reloadPackages:(NSArray *)packages {
     _trace();
     for (Package *package in packages)
         if (
@@ -6951,6 +7085,20 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _trace();
     [packages_ radixSortUsingFunction:reinterpret_cast<SKRadixFunction>(&PackageChangesRadix) withContext:NULL];
     _trace();
+}
+
+- (void) reloadData {
+    NSArray *packages = [database_ packages];
+
+    [packages_ removeAllObjects];
+    [sections_ removeAllObjects];
+
+    UIProgressHUD *hud([delegate_ addProgressHUD]);
+    // XXX: localize
+    [hud setText:@"Loading Changes"];
+    NSLog(@"HUD:%@::%@", delegate_, hud);
+    [self yieldToSelector:@selector(_reloadPackages:) withObject:packages];
+    [delegate_ removeProgressHUD:hud];
 
     Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
     Section *ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") localize:NO] autorelease];
@@ -6971,7 +7119,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             unseens = true;
             NSDate *seen;
 
-            _profile(ChangesView$reloadData$Remember)
+            _profile(ChangesController$reloadData$Remember)
                 seen = [package seen];
             _end
 
@@ -6986,7 +7134,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                     [name autorelease];
                 }
 
-                _profile(ChangesView$reloadData$Allocate)
+                _profile(ChangesController$reloadData$Allocate)
                     name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name];
                     section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease];
                     [sections_ addObject:section];
@@ -7018,528 +7166,771 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [sections_ insertObject:upgradable atIndex:0];
 
     [list_ reloadData];
-    [self reloadButtons];
+
+    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
+        initWithTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]]
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(upgradeButtonClicked)
+    ];
+    if (upgrades_ > 0) [[self navigationItem] setRightBarButtonItem:rightItem];
+    [rightItem release];
+
+    UIBarButtonItem *leftItem = [[UIBarButtonItem alloc]
+        initWithTitle:UCLocalize("REFRESH")
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(refreshButtonClicked)
+    ];
+    if (![delegate_ updating]) [[self navigationItem] setLeftBarButtonItem:leftItem];
+    [leftItem release];
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
+@end
+/* }}} */
+/* Search Controller {{{ */
+@interface SearchController : FilteredPackageController <
+    UISearchBarDelegate
+> {
+    UISearchBar *search_;
 }
 
-- (NSString *) leftButtonTitle {
-    return [(CYBook *)book_ updating] ? nil : UCLocalize("REFRESH");
+- (id) initWithDatabase:(Database *)database;
+- (void) reloadData;
+
+@end
+
+@implementation SearchController
+
+- (void) dealloc {
+    [search_ release];
+    [super dealloc];
 }
 
-- (id) rightButtonTitle {
-    return upgrades_ == 0 ? nil : [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]];
+- (void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
+    [packages_ setObject:[search_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)];
+    [search_ resignFirstResponder];
+    [self reloadData];
+}
+
+- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)text {
+    [packages_ setObject:text forFilter:@selector(isUnfilteredAndSelectedForBy:)];
+    [self reloadData];
+}
+
+- (NSString *) title { return nil; }
+
+- (id) initWithDatabase:(Database *)database {
+    return [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:nil];
 }
 
-- (NSString *) title {
-    return UCLocalize("CHANGES");
+- (void)viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    if (!search_) {
+        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];
+        [textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin];
+        [search_ setDelegate:self];
+        [textField setEnablesReturnKeyAutomatically:NO];
+        [[self navigationItem] setTitleView:textField];
+    }
+}
+
+- (void) _reloadData {
+}
+
+- (void) reloadData {
+    _profile(SearchController$reloadData)
+        [packages_ reloadData];
+    _end
+    PrintTimes();
+    [packages_ resetCursor];
+}
+
+- (void) didSelectPackage:(Package *)package {
+    [search_ resignFirstResponder];
+    [super didSelectPackage:package];
 }
 
 @end
 /* }}} */
-/* Search View {{{ */
-@protocol SearchViewDelegate
-- (void) showKeyboard:(BOOL)show;
-@end
-
-@interface SearchView : RVPage {
-    UIView *accessory_;
-    UISearchField *field_;
-    FilteredPackageTable *table_;
-    UIView *dimmed_;
-    bool reload_;
+/* Settings Controller {{{ */
+@interface SettingsController : CYViewController <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
+    _transient Database *database_;
+    NSString *name_;
+    Package *package_;
+    UITableView *table_;
+    id subscribedSwitch_;
+    id ignoredSwitch_;
+    UITableViewCell *subscribedCell_;
+    UITableViewCell *ignoredCell_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
-- (void) reloadData;
+- (id) initWithDatabase:(Database *)database package:(NSString *)package;
 
 @end
 
-@implementation SearchView
+@implementation SettingsController
 
 - (void) dealloc {
-    [field_ setDelegate:nil];
-
-    [accessory_ release];
-    [field_ release];
+    [name_ release];
+    if (package_ != nil)
+        [package_ release];
     [table_ release];
-    [dimmed_ release];
+    [subscribedSwitch_ release];
+    [ignoredSwitch_ release];
+    [subscribedCell_ release];
+    [ignoredCell_ release];
+
     [super dealloc];
 }
 
-- (void) _showKeyboard:(BOOL)show {
-    CGSize keysize = [UIKeyboard defaultSize];
-    CGRect keydown = [book_ pageBounds];
-    CGRect keyup = keydown;
-    keyup.size.height -= keysize.height - ButtonBarHeight_;
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
+    if (package_ == nil)
+        return 0;
 
-    float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
+    return 1;
+}
 
-    UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
-    [animation setSignificantRectFields:8];
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    if (package_ == nil)
+        return 0;
 
-    if (show) {
-        [animation setStartFrame:keydown];
-        [animation setEndFrame:keyup];
-    } else {
-        [animation setStartFrame:keyup];
-        [animation setEndFrame:keydown];
-    }
+    return 1;
+}
 
-    UIAnimator *animator = [UIAnimator sharedAnimator];
+- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
+    return UCLocalize("SHOW_ALL_CHANGES_EX");
+}
 
-    [animator
-        addAnimations:[NSArray arrayWithObjects:animation, nil]
-        withDuration:(KeyboardTime_ - delay)
-        start:!show
-    ];
+- (void) onSomething:(BOOL)value withKey:(NSString *)key {
+    if (package_ == nil)
+        return;
 
-    if (show)
-        [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
+    NSMutableDictionary *metadata([package_ metadata]);
 
-    [delegate_ showKeyboard:show];
+    BOOL before;
+    if (NSNumber *number = [metadata objectForKey:key])
+        before = [number boolValue];
+    else
+        before = NO;
+
+    if (value != before) {
+        [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
+        Changed_ = true;
+        [delegate_ updateData];
+    }
 }
 
-- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
-    [self _showKeyboard:YES];
+- (void) onSubscribed:(id)control {
+    [self onSomething:(int) [control isOn] withKey:@"IsSubscribed"];
 }
 
-- (void) textFieldDidResignFirstResponder:(UITextField *)field {
-    [self _showKeyboard:NO];
+- (void) onIgnored:(id)control {
+    [self onSomething:(int) [control isOn] withKey:@"IsIgnored"];
 }
 
-- (void) keyboardInputChanged:(UIFieldEditor *)editor {
-    if (reload_) {
-        NSString *text([field_ text]);
-        [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
-        [self reloadData];
-        reload_ = false;
+- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    if (package_ == nil)
+        return nil;
+
+    switch ([indexPath row]) {
+        case 0: return subscribedCell_;
+        case 1: return ignoredCell_;
+
+        _nodefault
     }
-}
 
-- (void) textFieldClearButtonPressed:(UITextField *)field {
-    reload_ = true;
+    return nil;
 }
 
-- (void) keyboardInputShouldDelete:(id)input {
-    reload_ = true;
+- (NSString *) title { return UCLocalize("SETTINGS"); }
+
+- (id) initWithDatabase:(Database *)database package:(NSString *)package {
+    if ((self = [super init])) {
+        database_ = database;
+        name_ = [package retain];
+
+        [[self navigationItem] setTitle:UCLocalize("SETTINGS")];
+
+        table_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped];
+        [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [table_ setAllowsSelection:NO];
+        [[self view] addSubview:table_];
+
+        subscribedSwitch_ = [[objc_getClass("UISwitch") alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
+        [subscribedSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+        [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventValueChanged];
+
+        ignoredSwitch_ = [[objc_getClass("UISwitch") alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
+        [ignoredSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+        [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventValueChanged];
+
+        subscribedCell_ = [[UITableViewCell alloc] init];
+        [subscribedCell_ setText:UCLocalize("SHOW_ALL_CHANGES")];
+        [subscribedCell_ setAccessoryView:subscribedSwitch_];
+
+        ignoredCell_ = [[UITableViewCell alloc] init];
+        [ignoredCell_ setText:UCLocalize("IGNORE_UPGRADES")];
+        [ignoredCell_ setAccessoryView:ignoredSwitch_];
+
+        [table_ setDataSource:self];
+        [table_ setDelegate:self];
+        [self reloadData];
+    } return self;
 }
 
-- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
-    if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
-        reload_ = true;
-        return YES;
-    } else {
-        [field_ resignFirstResponder];
-        return NO;
+- (void) reloadData {
+    if (package_ != nil)
+        [package_ autorelease];
+    package_ = [database_ packageWithName:name_];
+    if (package_ != nil) {
+        [package_ retain];
+        [subscribedSwitch_ setOn:([package_ subscribed] ? 1 : 0) animated:NO];
+        [ignoredSwitch_ setOn:([package_ ignored] ? 1 : 0) animated:NO];
     }
+
+    [table_ reloadData];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
-        CGRect pageBounds = [book_ pageBounds];
+@end
+/* }}} */
 
-        dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
-        CGColor dimmed(space_, 0, 0, 0, 0.5);
-        [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
+/* Signature Controller {{{ */
+@interface SignatureController : CYBrowserController {
+    _transient Database *database_;
+    NSString *package_;
+}
 
-        table_ = [[FilteredPackageTable alloc]
-            initWithBook:book
-            database:database
-            title:nil
-            filter:@selector(isUnfilteredAndSearchedForBy:)
-            with:nil
-        ];
+- (id) initWithDatabase:(Database *)database package:(NSString *)package;
 
-        [table_ setShouldHideHeaderInShortLists:NO];
-        [self addSubview:table_];
+@end
 
-        CGRect cnfrect = {{7, 38}, {17, 18}};
+@implementation SignatureController
 
-        CGRect area;
+- (void) dealloc {
+    [package_ release];
+    [super dealloc];
+}
 
-        area.origin.x = 10;
-        area.origin.y = 1;
+- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+    // XXX: dude!
+    [super webView:sender didClearWindowObject:window forFrame:frame];
+}
 
-        area.size.width = [self bounds].size.width - area.origin.x * 2;
-        area.size.height = [UISearchField defaultHeight];
+- (id) initWithDatabase:(Database *)database package:(NSString *)package {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+        package_ = [package retain];
+        [self reloadData];
+    } return self;
+}
 
-        field_ = [[UISearchField alloc] initWithFrame:area];
+- (void) reloadData {
+    [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
+}
 
-        UIFont *font = [UIFont systemFontOfSize:16];
-        [field_ setFont:font];
+@end
+/* }}} */
+/* Role Controller {{{ */
+@interface RoleController : CYViewController <
+    UITableViewDataSource,
+    UITableViewDelegate
+> {
+    _transient Database *database_;
+    id roledelegate_;
+    UITableView *table_;
+    UISegmentedControl *segment_;
+    UIView *container_;
+}
+
+- (void) showDoneButton;
+- (void) resizeSegmentedControl;
+
+@end
 
-        [field_ setPlaceholder:UCLocalize("SEARCH_EX")];
-        [field_ setDelegate:self];
+@implementation RoleController
+- (void) dealloc {
+    [table_ release];
+    [segment_ release];
+    [container_ release];
 
-        [field_ setPaddingTop:5];
+    [super dealloc];
+}
 
-        UITextInputTraits *traits([field_ textInputTraits]);
-        [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
-        [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
-        [traits setReturnKeyType:UIReturnKeySearch];
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
+    if ((self = [super init])) {
+        database_ = database;
+        roledelegate_ = delegate;
 
-        CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
+        [[self navigationItem] setTitle:UCLocalize("WHO_ARE_YOU")];
 
-        accessory_ = [[UIView alloc] initWithFrame:accrect];
-        [accessory_ addSubview:field_];
+        NSArray *items = [NSArray arrayWithObjects:
+            UCLocalize("USER"),
+            UCLocalize("HACKER"),
+            UCLocalize("DEVELOPER"),
+        nil];
+        segment_ = [[UISegmentedControl alloc] initWithItems:items];
+        container_ = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, 44.0f)];
+        [container_ addSubview:segment_];
+
+        int index = -1;
+        if ([Role_ isEqualToString:@"User"]) index = 0;
+        if ([Role_ isEqualToString:@"Hacker"]) index = 1;
+        if ([Role_ isEqualToString:@"Developer"]) index = 2;
+        if (index != -1) {
+            [segment_ setSelectedSegmentIndex:index];
+            [self showDoneButton];
+        }
 
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [segment_ addTarget:self action:@selector(segmentChanged:) forControlEvents:UIControlEventValueChanged];
+        [self resizeSegmentedControl];
+
+        table_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped];
+        [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [table_ setDelegate:self];
+        [table_ setDataSource:self];
+        [[self view] addSubview:table_];
+        [table_ reloadData];
     } return self;
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [table_ resetViewAnimated:animated];
+- (void) resizeSegmentedControl {
+    CGFloat width = [[self view] frame].size.width;
+    [segment_ setFrame:CGRectMake(width / 32.0f, 0, width - (width / 32.0f * 2.0f), 44.0f)];
 }
 
-- (void) _reloadData {
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+
+    [self resizeSegmentedControl];
 }
 
-- (void) reloadData {
-    [table_ setObject:[field_ text]];
-    _profile(SearchView$reloadData)
-        [table_ reloadData];
-    _end
-    PrintTimes();
-    [table_ resetCursor];
+- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
+    [self resizeSegmentedControl];
 }
 
-- (UIView *) accessoryView {
-    return accessory_;
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+    [self resizeSegmentedControl];
 }
 
-- (NSString *) title {
-    return nil;
+- (void) save {
+    NSString *role(nil);
+
+    switch ([segment_ selectedSegmentIndex]) {
+        case 0: role = @"User"; break;
+        case 1: role = @"Hacker"; break;
+        case 2: role = @"Developer"; break;
+
+        _nodefault
+    }
+
+    if (![role isEqualToString:Role_]) {
+        bool rolling(Role_ == nil);
+        Role_ = role;
+
+        Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+            Role_, @"Role",
+        nil];
+
+        [Metadata_ setObject:Settings_ forKey:@"Settings"];
+
+        Changed_ = true;
+
+        if (rolling)
+            [roledelegate_ loadData];
+        else
+            [roledelegate_ updateData];
+    }
 }
 
-- (NSString *) backButtonTitle {
-    return UCLocalize("SEARCH");
+- (void) segmentChanged:(UISegmentedControl *)control {
+    [self showDoneButton];
 }
 
-- (void) setDelegate:(id)delegate {
-    [table_ setDelegate:delegate];
-    [super setDelegate:delegate];
+- (void) doneButtonClicked {
+    [self save];
+    [[self navigationController] dismissModalViewControllerAnimated:YES];
+}
+
+- (void) showDoneButton {
+    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc]
+        initWithTitle:UCLocalize("DONE")
+        style:UIBarButtonItemStyleDone
+        target:self
+        action:@selector(doneButtonClicked)
+    ];
+    [[self navigationItem] setRightBarButtonItem:rightItem animated:[[self navigationItem] rightBarButtonItem] == nil];
+    [rightItem release];
+}
+
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
+    // XXX: For not having a single cell in the table, this sure is a lot of sections.
+    return 6;
+}
+
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    return 0; // :(
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    return nil; // This method is required by the protocol.
+}
+
+- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
+    if (section == 1)
+        return UCLocalize("ROLE_EX");
+    if (section == 4)
+        return [NSString stringWithFormat:
+            @"%@: %@\n%@: %@\n%@: %@",
+            UCLocalize("USER"), UCLocalize("USER_EX"),
+            UCLocalize("HACKER"), UCLocalize("HACKER_EX"),
+            UCLocalize("DEVELOPER"), UCLocalize("DEVELOPER_EX")
+        ];
+    else return nil;
+}
+
+- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
+    if (section == 3) return 44.0f;
+    else return 0;
+}
+
+- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
+    if (section == 3) return container_;
+    else return nil;
 }
 
 @end
 /* }}} */
-/* Settings View {{{ */
-@interface SettingsView : RVPage {
+
+/* Cydia Container {{{ */
+@interface CYContainer : UIViewController <ProgressDelegate> {
     _transient Database *database_;
-    NSString *name_;
-    Package *package_;
-    UIPreferencesTable *table_;
-    _UISwitchSlider *subscribedSwitch_;
-    _UISwitchSlider *ignoredSwitch_;
-    UIPreferencesControlTableCell *subscribedCell_;
-    UIPreferencesControlTableCell *ignoredCell_;
+    RefreshBar *refreshbar_;
+
+    bool dropped_;
+    bool updating_;
+    id updatedelegate_;
+    UITabBarController *root_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
+- (void) setTabBarController:(UITabBarController *)controller;
+
+- (void) dropBar:(BOOL)animated;
+- (void) beginUpdate;
+- (void) raiseBar:(BOOL)animated;
 
 @end
 
-@implementation SettingsView
+@implementation CYContainer
 
-- (void) dealloc {
-    [table_ setDataSource:nil];
+// NOTE: UIWindow only sends the top controller these messages,
+//       So we have to forward them on.
 
-    [name_ release];
-    if (package_ != nil)
-        [package_ release];
-    [table_ release];
-    [subscribedSwitch_ release];
-    [ignoredSwitch_ release];
-    [subscribedCell_ release];
-    [ignoredCell_ release];
-    [super dealloc];
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [root_ viewDidAppear:animated];
 }
 
-- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
-    if (package_ == nil)
-        return 0;
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    [root_ viewWillAppear:animated];
+}
 
-    return 2;
+- (void) viewDidDisappear:(BOOL)animated {
+    [super viewDidDisappear:animated];
+    [root_ viewDidDisappear:animated];
 }
 
-- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
-    if (package_ == nil)
-        return nil;
+- (void) viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    [root_ viewWillDisappear:animated];
+}
 
-    switch (group) {
-        case 0: return nil;
-        case 1: return nil;
+- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
+    return IsWildcat_;
+}
 
-        _nodefault
-    }
+- (void) setTabBarController:(UITabBarController *)controller {
+    root_ = controller;
+    [[self view] addSubview:[root_ view]];
+}
 
-    return nil;
+- (void) setUpdate:(NSDate *)date {
+    [self beginUpdate];
 }
 
-- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
-    if (package_ == nil)
-        return NO;
+- (void) beginUpdate {
+    [self dropBar:YES];
+    [refreshbar_ start];
 
-    switch (group) {
-        case 0: return NO;
-        case 1: return YES;
+    updating_ = true;
 
-        _nodefault
-    }
+    [NSThread
+        detachNewThreadSelector:@selector(performUpdate)
+        toTarget:self
+        withObject:nil
+    ];
+}
 
-    return NO;
+- (void) performUpdate { _pooled
+    Status status;
+    status.setDelegate(self);
+    [database_ updateWithStatus:status];
+
+    [self
+        performSelectorOnMainThread:@selector(completeUpdate)
+        withObject:nil
+        waitUntilDone:NO
+    ];
 }
 
-- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
-    if (package_ == nil)
-        return 0;
+- (void) completeUpdate {
+    updating_ = false;
 
-    switch (group) {
-        case 0: return 1;
-        case 1: return 1;
+    [self raiseBar:YES];
+    [refreshbar_ stop];
+    [updatedelegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+}
 
-        _nodefault
-    }
+- (void) cancelUpdate {
+    [refreshbar_ cancel];
+    [self completeUpdate];
+}
 
-    return 0;
+- (void) cancelPressed {
+    [self cancelUpdate];
 }
 
-- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
-    if (package_ == nil)
-        return;
+- (BOOL) updating {
+    return updating_;
+}
 
-    _UISwitchSlider *slider([cell control]);
-    BOOL value([slider value] != 0);
-    NSMutableDictionary *metadata([package_ metadata]);
+- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
+    [refreshbar_ setPrompt:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]];
+}
 
-    BOOL before;
-    if (NSNumber *number = [metadata objectForKey:key])
-        before = [number boolValue];
-    else
-        before = NO;
+- (void) startProgress {
+}
 
-    if (value != before) {
-        [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
-        Changed_ = true;
-        [delegate_ updateData];
-    }
+- (void) setProgressTitle:(NSString *)title {
+    [self
+        performSelectorOnMainThread:@selector(_setProgressTitle:)
+        withObject:title
+        waitUntilDone:YES
+    ];
 }
 
-- (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
-    [self onSomething:cell withKey:@"IsSubscribed"];
+- (bool) isCancelling:(size_t)received {
+    return !updating_;
 }
 
-- (void) onIgnored:(UIPreferencesControlTableCell *)cell {
-    [self onSomething:cell withKey:@"IsIgnored"];
+- (void) setProgressPercent:(float)percent {
+    [self
+        performSelectorOnMainThread:@selector(_setProgressPercent:)
+        withObject:[NSNumber numberWithFloat:percent]
+        waitUntilDone:YES
+    ];
 }
 
-- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
-    if (package_ == nil)
-        return nil;
-
-    switch (group) {
-        case 0: switch (row) {
-            case 0:
-                return subscribedCell_;
-            case 1:
-                return ignoredCell_;
-            _nodefault
-        } break;
-
-        case 1: switch (row) {
-            case 0: {
-                UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
-                [cell setShowSelection:NO];
-                [cell setTitle:UCLocalize("SHOW_ALL_CHANGES_EX")];
-                return cell;
-            }
-
-            _nodefault
-        } break;
+- (void) addProgressOutput:(NSString *)output {
+    [self
+        performSelectorOnMainThread:@selector(_addProgressOutput:)
+        withObject:output
+        waitUntilDone:YES
+    ];
+}
 
-        _nodefault
-    }
+- (void) _setProgressTitle:(NSString *)title {
+    [refreshbar_ setPrompt:title];
+}
 
-    return nil;
+- (void) _setProgressPercent:(NSNumber *)percent {
+    [refreshbar_ setProgress:[percent floatValue]];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
-    if ((self = [super initWithBook:book])) {
-        database_ = database;
-        name_ = [package retain];
+- (void) _addProgressOutput:(NSString *)output {
+}
 
-        table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
-        [self addSubview:table_];
+- (void) setUpdateDelegate:(id)delegate {
+    updatedelegate_ = delegate;
+}
 
-        subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
-        [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventTouchUpInside];
+- (void) dropBar:(BOOL)animated {
+    if (dropped_) return;
+    dropped_ = true;
 
-        ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
-        [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventTouchUpInside];
+    [[self view] addSubview:refreshbar_];
 
-        subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
-        [subscribedCell_ setShowSelection:NO];
-        [subscribedCell_ setTitle:UCLocalize("SHOW_ALL_CHANGES")];
-        [subscribedCell_ setControl:subscribedSwitch_];
+    if (animated) [UIView beginAnimations:nil context:NULL];
+    CGRect barframe = [refreshbar_ frame];
+    CGRect viewframe = [[root_ view] frame];
+    viewframe.origin.y += barframe.size.height;
+    viewframe.size.height -= barframe.size.height;
+    [[root_ view] setFrame:viewframe];
+    if (animated) [UIView commitAnimations];
 
-        ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
-        [ignoredCell_ setShowSelection:NO];
-        [ignoredCell_ setTitle:UCLocalize("IGNORE_UPGRADES")];
-        [ignoredCell_ setControl:ignoredSwitch_];
+    // Ensure bar has the proper width for our view, it might have changed
+    barframe.size.width = viewframe.size.width;
+    [refreshbar_ setFrame:barframe];
 
-        [table_ setDataSource:self];
-        [self reloadData];
-    } return self;
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [table_ resetViewAnimated:animated];
-}
+- (void) raiseBar:(BOOL)animated {
+    if (!dropped_) return;
+    dropped_ = false;
 
-- (void) reloadData {
-    if (package_ != nil)
-        [package_ autorelease];
-    package_ = [database_ packageWithName:name_];
-    if (package_ != nil) {
-        [package_ retain];
-        [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
-        [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
-    }
+    [refreshbar_ removeFromSuperview];
 
-    [table_ reloadData];
-}
+    if (animated) [UIView beginAnimations:nil context:NULL];
+    CGRect barframe = [refreshbar_ frame];
+    CGRect viewframe = [[root_ view] frame];
+    viewframe.origin.y -= barframe.size.height;
+    viewframe.size.height += barframe.size.height;
+    [[root_ view] setFrame:viewframe];
+    if (animated) [UIView commitAnimations];
 
-- (NSString *) title {
-    return UCLocalize("SETTINGS");
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
 }
 
-@end
-/* }}} */
-
-/* Signature View {{{ */
-@interface SignatureView : CydiaBrowserView {
-    _transient Database *database_;
-    NSString *package_;
+- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
-
-@end
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+    if (dropped_) {
+        [self raiseBar:NO];
+        [self dropBar:NO];
+    }
 
-@implementation SignatureView
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
 
 - (void) dealloc {
-    [package_ release];
+    [refreshbar_ release];
     [super dealloc];
 }
 
-- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
-    // XXX: dude!
-    [super webView:sender didClearWindowObject:window forFrame:frame];
-}
-
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
         database_ = database;
-        package_ = [package retain];
-        [self reloadData];
-    } return self;
-}
 
-- (void) resetViewAnimated:(BOOL)animated {
-}
+        [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
 
-- (void) reloadData {
-    [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
+        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,
+    kChangesTag = 2,
+    kManageTag = 3,
+    kInstalledTag = 4,
+    kSourcesTag = 5,
+    kSearchTag = 6
+} CYTabTag;
+
 @interface Cydia : UIApplication <
-    ConfirmationViewDelegate,
-    ProgressViewDelegate,
-    SearchViewDelegate,
-    CydiaDelegate
+    ConfirmationControllerDelegate,
+    ProgressControllerDelegate,
+    CydiaDelegate,
+    UINavigationControllerDelegate
 > {
     UIWindow *window_;
+    CYContainer *container_;
 
-    UIView *underlay_;
-    UIView *overlay_;
-    CYBook *book_;
-    UIToolbar *toolbar_;
-
-    RVBook *confirm_;
+    id tabbar_;
 
     NSMutableArray *essential_;
     NSMutableArray *broken_;
 
     Database *database_;
-    ProgressView *progress_;
 
-    unsigned tag_;
+    int tag_;
 
     UIKeyboard *keyboard_;
     UIProgressHUD *hud_;
 
-    SectionsView *sections_;
-    ChangesView *changes_;
-    ManageView *manage_;
-    SearchView *search_;
+    SectionsController *sections_;
+    ChangesController *changes_;
+    ManageController *manage_;
+    SearchController *search_;
+    SourceTable *sources_;
+    InstalledController *installed_;
+    id queueDelegate_;
 
 #if RecyclePackageViews
     NSMutableArray *details_;
 #endif
+
+    bool loaded_;
 }
 
-- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class;
-- (void) setPage:(RVPage *)page;
+- (CYViewController *) _pageForURL:(NSURL *)url withClass:(Class)_class;
+- (void) setPage:(CYViewController *)page;
+- (void) loadData;
 
 @end
 
 static _finline void _setHomePage(Cydia *self) {
-    [self setPage:[self _pageForURL:[NSURL URLWithString:CydiaURL(@"")] withClass:[HomeView class]]];
+    [self setPage:[self _pageForURL:[NSURL URLWithString:CydiaURL(@"")] withClass:[HomeController class]]];
 }
 
 @implementation Cydia
 
+- (void) beginUpdate {
+    [container_ beginUpdate];
+}
+
+- (BOOL) updating {
+    return [container_ updating];
+}
+
+- (UIView *) rotatingContentViewForWindow:(UIWindow *)window {
+    return window_;
+}
+
 - (void) _loaded {
     if ([broken_ count] != 0) {
         int count = [broken_ count];
 
-        UIActionSheet *sheet = [[[UIActionSheet alloc]
+        UIAlertView *alert = [[[UIAlertView alloc]
             initWithTitle:(count == 1 ? UCLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:UCLocalize("HALFINSTALLED_PACKAGES"), count])
-            buttons:[NSArray arrayWithObjects:
-                UCLocalize("FORCIBLY_CLEAR"),
-                UCLocalize("TEMPORARY_IGNORE"),
-            nil]
-            defaultButtonIndex:0
+            message:UCLocalize("HALFINSTALLED_PACKAGE_EX")
             delegate:self
-            context:@"fixhalf"
+            cancelButtonTitle:UCLocalize("FORCIBLY_CLEAR")
+            otherButtonTitles:UCLocalize("TEMPORARY_IGNORE"), nil
         ] autorelease];
 
-        [sheet setBodyText:UCLocalize("HALFINSTALLED_PACKAGE_EX")];
-        [sheet popupAlertAnimated:YES];
+        [alert setContext:@"fixhalf"];
+        [alert show];
     } else if (!Ignored_ && [essential_ count] != 0) {
         int count = [essential_ count];
 
-        UIActionSheet *sheet = [[[UIActionSheet alloc]
+        UIAlertView *alert = [[[UIAlertView alloc]
             initWithTitle:(count == 1 ? UCLocalize("ESSENTIAL_UPGRADE") : [NSString stringWithFormat:UCLocalize("ESSENTIAL_UPGRADES"), count])
-            buttons:[NSArray arrayWithObjects:
-                UCLocalize("UPGRADE_ESSENTIAL"),
-                UCLocalize("COMPLETE_UPGRADE"),
-                UCLocalize("TEMPORARY_IGNORE"),
-            nil]
-            defaultButtonIndex:0
+            message:UCLocalize("ESSENTIAL_UPGRADE_EX")
             delegate:self
-            context:@"upgrade"
+            cancelButtonTitle:UCLocalize("TEMPORARY_IGNORE")
+            otherButtonTitles:UCLocalize("UPGRADE_ESSENTIAL"), UCLocalize("COMPLETE_UPGRADE"), nil
         ] autorelease];
 
-        [sheet setBodyText:UCLocalize("ESSENTIAL_UPGRADE_EX")];
-        [sheet popupAlertAnimated:YES];
+        [alert setContext:@"upgrade"];
+        [alert show];
     }
 }
 
@@ -7566,22 +7957,73 @@ static _finline void _setHomePage(Cydia *self) {
     [self _saveConfig];
 
     /* XXX: this is just stupid */
-    if (tag_ != 2 && sections_ != nil)
+    if (tag_ != 1 && sections_ != nil)
         [sections_ reloadData];
-    if (tag_ != 3 && changes_ != nil)
+    if (tag_ != 2 && changes_ != nil)
         [changes_ reloadData];
-    if (tag_ != 5 && search_ != nil)
+    if (tag_ != 4 && search_ != nil)
         [search_ reloadData];
 
-    [book_ reloadData];
+    [(CYNavigationController *)[tabbar_ selectedViewController] reloadData];
 }
 
-- (void) _reloadData {
-    UIView *block();
+- (int)indexOfTabWithTag:(int)tag {
+    int i = 0;
+    for (UINavigationController *controller in [tabbar_ viewControllers]) {
+        if ([[controller tabBarItem] tag] == tag) return i;
+        i += 1;
+    }
+
+    return -1;
+}
+
+- (void) _refreshIfPossible {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
-    static bool loaded(false);
+    SCNetworkReachabilityFlags flags; {
+        SCNetworkReachabilityRef reachability(SCNetworkReachabilityCreateWithName(NULL, "cydia.saurik.com"));
+        SCNetworkReachabilityGetFlags(reachability, &flags);
+        CFRelease(reachability);
+    }
+
+    // XXX: this elaborate mess is what Apple is using to determine this? :(
+    // XXX: do we care if the user has to intervene? maybe that's ok?
+    bool reachable(
+        (flags & kSCNetworkReachabilityFlagsReachable) != 0 && (
+            (flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0 || (
+                (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0 ||
+                (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0
+            ) && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0 ||
+            (flags & kSCNetworkReachabilityFlagsIsWWAN) != 0
+        )
+    );
+
+    if (loaded_ || ManualRefresh || !reachable) loaded:
+        [self performSelectorOnMainThread:@selector(_loaded) withObject:nil waitUntilDone:NO];
+    else {
+        loaded_ = true;
+
+        NSDate *update([Metadata_ objectForKey:@"LastUpdate"]);
+
+        if (update != nil) {
+            NSTimeInterval interval([update timeIntervalSinceNow]);
+            if (interval <= 0 && interval > -(15*60))
+                goto loaded;
+        }
+
+        [container_ performSelectorOnMainThread:@selector(setUpdate:) withObject:update waitUntilDone:NO];
+    }
+
+    [pool release];
+}
+
+- (void) refreshIfPossible {
+    [NSThread detachNewThreadSelector:@selector(_refreshIfPossible) toTarget:self withObject:nil];
+}
+
+- (void) _reloadData {
     UIProgressHUD *hud([self addProgressHUD]);
-    [hud setText:(loaded ? UCLocalize("RELOADING_DATA") : UCLocalize("LOADING_DATA"))];
+    [hud setText:(loaded_ ? UCLocalize("RELOADING_DATA") : UCLocalize("LOADING_DATA"))];
 
     [database_ yieldToSelector:@selector(reloadData) withObject:nil];
     _trace();
@@ -7606,41 +8048,26 @@ static _finline void _setHomePage(Cydia *self) {
 
     if (changes != 0) {
         NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
-        [toolbar_ setBadgeValue:badge forButton:3];
-        if ([toolbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
-            [toolbar_ setBadgeAnimated:([essential_ count] != 0) forButton:3];
+        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setBadgeValue:badge];
+        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setAnimatedBadge:YES];
+
         if ([self respondsToSelector:@selector(setApplicationBadge:)])
             [self setApplicationBadge:badge];
         else
             [self setApplicationBadgeString:badge];
     } else {
-        [toolbar_ setBadgeValue:nil forButton:3];
-        if ([toolbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
-            [toolbar_ setBadgeAnimated:NO forButton:3];
+        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setBadgeValue:nil];
+        [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kChangesTag]] tabBarItem] setAnimatedBadge:NO];
+
         if ([self respondsToSelector:@selector(removeApplicationBadge)])
             [self removeApplicationBadge];
         else // XXX: maybe use setApplicationBadgeString also?
             [self setApplicationIconBadgeNumber:0];
     }
 
-    Queuing_ = false;
-    [toolbar_ setBadgeValue:nil forButton:4];
-
     [self _updateData];
 
-    if (loaded || ManualRefresh) loaded:
-        [self _loaded];
-    else {
-        loaded = true;
-
-        if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
-            NSTimeInterval interval([update timeIntervalSinceNow]);
-            if (interval <= 0 && interval > -(15*60))
-                goto loaded;
-        }
-
-        [book_ update];
-    }
+    [self refreshIfPossible];
 }
 
 - (void) updateData {
@@ -7670,7 +8097,12 @@ static _finline void _setHomePage(Cydia *self) {
 
     [self _saveConfig];
 
-    [progress_
+    ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease];
+    CYNavigationController *navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
+    if (IsWildcat_) [navigation setModalPresentationStyle:UIModalPresentationFormSheet];
+    [container_ presentModalViewController:navigation animated:YES];
+
+    [progress
         detachNewThreadSelector:@selector(update_)
         toTarget:self
         withObject:nil
@@ -7680,8 +8112,7 @@ static _finline void _setHomePage(Cydia *self) {
 
 - (void) reloadData {
     @synchronized (self) {
-        if (confirm_ == nil)
-            [self _reloadData];
+        [self _reloadData];
     }
 }
 
@@ -7693,26 +8124,21 @@ static _finline void _setHomePage(Cydia *self) {
         _error->Discard();
 }
 
-- (void) popUpBook:(RVBook *)book {
-    [underlay_ popSubview:book];
-}
-
 - (CGRect) popUpBounds {
-    return [underlay_ bounds];
+    return [[tabbar_ view] bounds];
 }
 
 - (bool) perform {
     if (![database_ prepare])
         return false;
 
-    confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
-    [confirm_ setDelegate:self];
-
-    ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
+    ConfirmationController *page([[[ConfirmationController alloc] initWithDatabase:database_] autorelease]);
     [page setDelegate:self];
+    CYNavigationController *confirm_ = [[CYNavigationController alloc] initWithRootViewController:page];
+    [confirm_ setDelegate:self];
 
-    [confirm_ setPage:page];
-    [self popUpBook:confirm_];
+    if (IsWildcat_) [confirm_ setModalPresentationStyle:UIModalPresentationFormSheet];
+    [container_ presentModalViewController:confirm_ animated:YES];
 
     return true;
 }
@@ -7731,6 +8157,15 @@ static _finline void _setHomePage(Cydia *self) {
     }
 }
 
+- (void) installPackages:(NSArray *)packages {
+    @synchronized (self) {
+        for (Package *package in packages)
+            [package install];
+        [self resolve];
+        [self perform];
+    }
+}
+
 - (void) installPackage:(Package *)package {
     @synchronized (self) {
         [package install];
@@ -7755,32 +8190,24 @@ static _finline void _setHomePage(Cydia *self) {
     }
 }
 
-- (void) cancel {
-    [self slideUp:[[[UIActionSheet alloc]
-        initWithTitle:nil
-        buttons:[NSArray arrayWithObjects:UCLocalize("CONTINUE_QUEUING"), UCLocalize("CANCEL_CLEAR"), nil]
-        defaultButtonIndex:1
-        delegate:self
-        context:@"cancel"
-    ] autorelease]];
-}
-
 - (void) complete {
     @synchronized (self) {
         [self _reloadData];
-
-        if (confirm_ != nil) {
-            [confirm_ release];
-            confirm_ = nil;
-        }
     }
 }
 
-- (void) confirm {
-    [overlay_ removeFromSuperview];
-    reload_ = true;
+- (void) confirmWithNavigationController:(UINavigationController *)navigation {
+    ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease];
+
+    if (navigation != nil) {
+        [navigation pushViewController:progress animated:YES];
+    } else {
+        navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
+        if (IsWildcat_) [navigation setModalPresentationStyle:UIModalPresentationFormSheet];
+        [container_ presentModalViewController:navigation animated:YES];
+    }
 
-    [progress_
+    [progress
         detachNewThreadSelector:@selector(perform)
         toTarget:database_
         withObject:nil
@@ -7788,92 +8215,102 @@ static _finline void _setHomePage(Cydia *self) {
     ];
 }
 
-- (void) progressViewIsComplete:(ProgressView *)progress {
-    if (confirm_ != nil) {
-        [underlay_ addSubview:overlay_];
-        [confirm_ popFromSuperviewAnimated:NO];
-    }
-
+- (void) progressControllerIsComplete:(ProgressController *)progress {
     [self complete];
 }
 
-- (void) setPage:(RVPage *)page {
-    [page resetViewAnimated:NO];
+- (void) setPage:(CYViewController *)page {
     [page setDelegate:self];
-    [book_ setPage:page];
+
+    CYNavigationController *navController = (CYNavigationController *) [tabbar_ selectedViewController];
+    [navController setViewControllers:[NSArray arrayWithObject:page] animated:NO];
+    for (CYNavigationController *page in [tabbar_ viewControllers]) {
+        if (page != navController) [page setViewControllers:nil];
+    }
 }
 
-- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
-    CydiaBrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
+- (CYViewController *) _pageForURL:(NSURL *)url withClass:(Class)_class {
+    CYBrowserController *browser = [[[_class alloc] init] autorelease];
     [browser loadURL:url];
     return browser;
 }
 
-- (SectionsView *) sectionsView {
+- (SectionsController *) sectionsController {
     if (sections_ == nil)
-        sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
+        sections_ = [[SectionsController alloc] initWithDatabase:database_];
     return sections_;
 }
 
-- (void) buttonBarItemTapped:(id)sender {
-    unsigned tag = [sender tag];
-    if (tag == tag_) {
-        [book_ resetViewAnimated:YES];
-        return;
-    } else if (tag_ == 2)
-        [[self sectionsView] resetView];
-
-    switch (tag) {
-        case 1: _setHomePage(self); break;
-
-        case 2: [self setPage:[self sectionsView]]; break;
-        case 3: [self setPage:changes_]; break;
-        case 4: [self setPage:manage_]; break;
-        case 5: [self setPage:search_]; break;
+- (ChangesController *) changesController {
+    if (changes_ == nil)
+        changes_ = [[ChangesController alloc] initWithDatabase:database_ delegate:self];
+    return changes_;
+}
 
-        _nodefault
+- (ManageController *) manageController {
+    if (manage_ == nil) {
+        manage_ = (ManageController *) [[self
+            _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
+            withClass:[ManageController class]
+        ] retain];
+        if (!IsWildcat_) queueDelegate_ = manage_;
     }
+    return manage_;
+}
 
-    tag_ = tag;
+- (SearchController *) searchController {
+    if (search_ == nil)
+        search_ = [[SearchController alloc] initWithDatabase:database_];
+    return search_;
 }
 
-- (void) askForSettings {
-    NSString *parenthetical(UCLocalize("PARENTHETICAL"));
+- (SourceTable *) sourcesController {
+    if (sources_ == nil)
+        sources_ = [[SourceTable alloc] initWithDatabase:database_];
+    return sources_;
+}
 
-    CYActionSheet *role([[[CYActionSheet alloc]
-        initWithTitle:UCLocalize("WHO_ARE_YOU")
-        buttons:[NSArray arrayWithObjects:
-            [NSString stringWithFormat:parenthetical, UCLocalize("USER"), UCLocalize("USER_EX")],
-            [NSString stringWithFormat:parenthetical, UCLocalize("HACKER"), UCLocalize("HACKER_EX")],
-            [NSString stringWithFormat:parenthetical, UCLocalize("DEVELOPER"), UCLocalize("DEVELOPER_EX")],
-        nil]
-        defaultButtonIndex:-1
-    ] autorelease]);
+- (InstalledController *) installedController {
+    if (installed_ == nil) {
+        installed_ = [[InstalledController alloc] initWithDatabase:database_];
+        if (IsWildcat_) queueDelegate_ = installed_;
+    }
+    return installed_;
+}
 
-    [role setBodyText:UCLocalize("ROLE_EX")];
+- (void) tabBarController:(id)tabBarController didSelectViewController:(UIViewController *)viewController {
+    int tag = [[viewController tabBarItem] tag];
+    if (tag == tag_) {
+        [(CYNavigationController *)[tabbar_ selectedViewController] popToRootViewControllerAnimated:YES];
+        return;
+    } else if (tag_ == 1) {
+        [[self sectionsController] resetView];
+    }
 
-    int button([role yieldToPopupAlertAnimated:YES]);
+    switch (tag) {
+        case kCydiaTag: _setHomePage(self); break;
 
-    switch (button) {
-        case 1: Role_ = @"User"; break;
-        case 2: Role_ = @"Hacker"; break;
-        case 3: Role_ = @"Developer"; break;
+        case kSectionsTag: [self setPage:[self sectionsController]]; break;
+        case kChangesTag: [self setPage:[self changesController]]; break;
+        case kManageTag: [self setPage:[self manageController]]; break;
+        case kInstalledTag: [self setPage:[self installedController]]; break;
+        case kSourcesTag: [self setPage:[self sourcesController]]; break;
+        case kSearchTag: [self setPage:[self searchController]]; break;
 
         _nodefault
     }
 
-    Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-        Role_, @"Role",
-    nil];
-
-    [Metadata_ setObject:Settings_ forKey:@"Settings"];
-
-    Changed_ = true;
+    tag_ = tag;
+}
 
-    [role dismiss];
+- (void) showSettings {
+    RoleController *role = [[RoleController alloc] initWithDatabase:database_ delegate:self];
+    CYNavigationController *nav = [[CYNavigationController alloc] initWithRootViewController:role];
+    if (IsWildcat_) [nav setModalPresentationStyle:UIModalPresentationFormSheet];
+    [container_ presentModalViewController:nav animated:YES];
 }
 
-- (void) setPackageView:(PackageView *)view {
+- (void) setPackageController:(PackageController *)view {
     WebThreadLock();
     [view setPackage:nil];
 #if RecyclePackageViews
@@ -7883,19 +8320,19 @@ static _finline void _setHomePage(Cydia *self) {
     WebThreadUnlock();
 }
 
-- (PackageView *) _packageView {
-    return [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
+- (PackageController *) _packageController {
+    return [[[PackageController alloc] initWithDatabase:database_] autorelease];
 }
 
-- (PackageView *) packageView {
+- (PackageController *) packageController {
 #if RecyclePackageViews
-    PackageView *view;
+    PackageController *view;
     size_t count([details_ count]);
 
     if (count == 0) {
-        view = [self _packageView];
+        view = [self _packageController];
       renew:
-        [details_ addObject:[self _packageView]];
+        [details_ addObject:[self _packageController]];
     } else {
         view = [[[details_ lastObject] retain] autorelease];
         [details_ removeLastObject];
@@ -7905,98 +8342,81 @@ static _finline void _setHomePage(Cydia *self) {
 
     return view;
 #else
-    return [self _packageView];
+    return [self _packageController];
 #endif
 }
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
-
-    if ([context isEqualToString:@"missing"])
-        [sheet dismiss];
-    else if ([context isEqualToString:@"cancel"]) {
-        bool clear;
+- (void) cancelAndClear:(bool)clear {
+    @synchronized (self) {
+        if (clear) {
+            // Clear all marks.
+            pkgCacheFile &cache([database_ cache]);
+            for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
+                // Unmark method taken from Synaptic Package Manager.
+                               // Thanks for being sane, unlike Aptitude.
+                               if (!cache[iterator].Keep()) {
+                                       cache->MarkKeep(iterator, false);
+                                       cache->SetReInstall(iterator, false);
+                               }
+            }
 
-        switch (button) {
-            case 1:
-                clear = false;
-            break;
+                       // Stop queuing, and let the appropriate controller know it.
+            Queuing_ = false;
+            [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kManageTag] != -1 ? [self indexOfTabWithTag:kManageTag] : [self indexOfTabWithTag:kInstalledTag]] tabBarItem] setBadgeValue:nil];
+            [queueDelegate_ queueStatusDidChange];
+        } else {
+                       // Start queuing, and let the controllers know.
+            Queuing_ = true;
 
-            case 2:
-                clear = true;
-            break;
+            [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kManageTag] != -1 ? [self indexOfTabWithTag:kManageTag] : [self indexOfTabWithTag:kInstalledTag]] tabBarItem] setBadgeValue:UCLocalize("Q_D")];
+            [(CYNavigationController *)[tabbar_ selectedViewController] reloadData];
 
-            _nodefault
+            [queueDelegate_ queueStatusDidChange];
         }
+    }
+}
 
-        [sheet dismiss];
+- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+    NSString *context([alert context]);
 
-        @synchronized (self) {
-            if (clear)
-                [self _reloadData];
-            else {
-                Queuing_ = true;
-                [toolbar_ setBadgeValue:UCLocalize("Q_D") forButton:4];
-                [book_ reloadData];
-            }
+    if ([context isEqualToString:@"fixhalf"]) {
+        if (button == [alert firstOtherButtonIndex]) {
+            @synchronized (self) {
+                for (Package *broken in broken_) {
+                    [broken remove];
 
-            if (confirm_ != nil) {
-                [confirm_ release];
-                confirm_ = nil;
-            }
-        }
-    } else if ([context isEqualToString:@"fixhalf"]) {
-        switch (button) {
-            case 1:
-                @synchronized (self) {
-                    for (Package *broken in broken_) {
-                        [broken remove];
-
-                        NSString *id = [broken id];
-                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
-                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
-                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
-                        unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
-                    }
-
-                    [self resolve];
-                    [self perform];
+                    NSString *id = [broken id];
+                    unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
+                    unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
+                    unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
+                    unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
                 }
-            break;
 
-            case 2:
-                [broken_ removeAllObjects];
-                [self _loaded];
-            break;
-
-            _nodefault
+                [self resolve];
+                [self perform];
+            }
+        } else if (button == [alert cancelButtonIndex]) {
+            [broken_ removeAllObjects];
+            [self _loaded];
         }
 
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     } else if ([context isEqualToString:@"upgrade"]) {
-        switch (button) {
-            case 1:
-                @synchronized (self) {
-                    for (Package *essential in essential_)
-                        [essential install];
-
-                    [self resolve];
-                    [self perform];
-                }
-            break;
-
-            case 2:
-                [self distUpgrade];
-            break;
-
-            case 3:
-                Ignored_ = YES;
-            break;
+        if (button == [alert firstOtherButtonIndex]) {
+            @synchronized (self) {
+                for (Package *essential in essential_)
+                    [essential install];
 
-            _nodefault
+                [self resolve];
+                [self perform];
+            }
+        } else if (button == [alert firstOtherButtonIndex] + 1) {
+            [self distUpgrade];
+        } else if (button == [alert cancelButtonIndex]) {
+            Ignored_ = YES;
         }
 
-        [sheet dismiss];
+        [alert dismissWithClickedButtonIndex:-1 animated:YES];
     }
 }
 
@@ -8010,7 +8430,9 @@ static _finline void _setHomePage(Cydia *self) {
 }
 
 - (void) applicationSuspend:(__GSEvent *)event {
-    if (hud_ == nil && ![progress_ isRunning])
+    // FIXME: This needs to be fixed, but we no longer have a progress_.
+    //        What's the best solution?
+    if (hud_ == nil)// && ![progress_ isRunning])
         [super applicationSuspend:event];
 }
 
@@ -8026,9 +8448,11 @@ static _finline void _setHomePage(Cydia *self) {
 
 - (UIProgressHUD *) addProgressHUD {
     UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]);
+    [hud setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+
     [window_ setUserInteractionEnabled:NO];
     [hud show:YES];
-    [progress_ addSubview:hud];
+    [[container_ view] addSubview:hud];
     return hud;
 }
 
@@ -8038,21 +8462,21 @@ static _finline void _setHomePage(Cydia *self) {
     [window_ setUserInteractionEnabled:YES];
 }
 
-- (RVPage *) pageForPackage:(NSString *)name {
+- (CYViewController *) pageForPackage:(NSString *)name {
     if (Package *package = [database_ packageWithName:name]) {
-        PackageView *view([self packageView]);
+        PackageController *view([self packageController]);
         [view setPackage:package];
         return view;
     } else {
         NSURL *url([NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"unknown" ofType:@"html"]]);
         url = [NSURL URLWithString:[[url absoluteString] stringByAppendingString:[NSString stringWithFormat:@"?%@", name]]];
-        return [self _pageForURL:url withClass:[CydiaBrowserView class]];
+        return [self _pageForURL:url withClass:[CYBrowserController class]];
     }
 }
 
-- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
+- (CYViewController *) pageForURL:(NSURL *)url hasTag:(int *)tag {
     if (tag != NULL)
-        tag = 0;
+        *tag = -1;
 
     NSString *href([url absoluteString]);
     if ([href hasPrefix:@"apptapp://package/"])
@@ -8069,28 +8493,28 @@ static _finline void _setHomePage(Cydia *self) {
         path = [@"/" stringByAppendingString:path];
 
     if ([path isEqualToString:@"/add-source"])
-        return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
+        return [[[AddSourceController alloc] initWithDatabase:database_] autorelease];
     else if ([path isEqualToString:@"/storage"])
-        return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[CydiaBrowserView class]];
+        return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[CYBrowserController class]];
     else if ([path isEqualToString:@"/sources"])
-        return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
+        return [[[SourceTable alloc] initWithDatabase:database_] autorelease];
     else if ([path isEqualToString:@"/packages"])
-        return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
+        return [[[InstalledController alloc] initWithDatabase:database_] autorelease];
     else if ([path hasPrefix:@"/url/"])
-        return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[CydiaBrowserView class]];
+        return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[CYBrowserController class]];
     else if ([path hasPrefix:@"/launch/"])
         [self launchApplicationWithIdentifier:[path substringFromIndex:8] suspended:NO];
     else if ([path hasPrefix:@"/package-settings/"])
-        return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:18]] autorelease];
+        return [[[SettingsController alloc] initWithDatabase:database_ package:[path substringFromIndex:18]] autorelease];
     else if ([path hasPrefix:@"/package-signature/"])
-        return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[path substringFromIndex:19]] autorelease];
+        return [[[SignatureController alloc] initWithDatabase:database_ package:[path substringFromIndex:19]] autorelease];
     else if ([path hasPrefix:@"/package/"])
         return [self pageForPackage:[path substringFromIndex:9]];
     else if ([path hasPrefix:@"/files/"]) {
         NSString *name = [path substringFromIndex:7];
 
         if (Package *package = [database_ packageWithName:name]) {
-            FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
+            FileTable *files = [[[FileTable alloc] initWithDatabase:database_] autorelease];
             [files setPackage:package];
             return files;
         }
@@ -8102,15 +8526,23 @@ static _finline void _setHomePage(Cydia *self) {
 - (void) applicationOpenURL:(NSURL *)url {
     [super applicationOpenURL:url];
     int tag;
-    if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
+    if (CYViewController *page = [self pageForURL:url hasTag:&tag]) {
         [self setPage:page];
-        [toolbar_ showSelectionForButton:tag];
         tag_ = tag;
+        [tabbar_ setSelectedViewController:(tag_ == -1 ? nil : [[tabbar_ viewControllers] objectAtIndex:tag_])];
     }
 }
 
+- (void) applicationWillResignActive:(UIApplication *)application {
+    // Stop refreshing if you get a phone call or lock the device.
+    if ([container_ updating]) [container_ cancelUpdate];
+
+    if ([[self superclass] instancesRespondToSelector:@selector(applicationWillResignActive:)])
+        [super applicationWillResignActive:application];
+}
+
 - (void) applicationDidFinishLaunching:(id)unused {
-    [BrowserView _initialize];
+    [CYBrowserController _initialize];
 
     [NSURLProtocol registerClass:[CydiaURLProtocol class]];
 
@@ -8120,27 +8552,20 @@ static _finline void _setHomePage(Cydia *self) {
     Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
     Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
 
-    tag_ = 1;
+    tag_ = 0;
 
     essential_ = [[NSMutableArray alloc] initWithCapacity:4];
     broken_ = [[NSMutableArray alloc] initWithCapacity:4];
 
-    window_ = [[UIWindow alloc] initWithContentRect:[UIHardware fullScreenApplicationContentRect]];
+    UIScreen *screen([UIScreen mainScreen]);
+
+    window_ = [[UIWindow alloc] initWithFrame:[screen bounds]];
     [window_ orderFront:self];
     [window_ makeKey:self];
     [window_ setHidden:NO];
 
     database_ = [Database sharedInstance];
 
-    progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
-    [database_ setDelegate:progress_];
-    [window_ setContentView:progress_];
-
-    underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
-    [progress_ setContentView:underlay_];
-
-    [progress_ resetView];
-
     if (
         readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
@@ -8156,7 +8581,7 @@ static _finline void _setHomePage(Cydia *self) {
         [self setIdleTimerDisabled:YES];
 
         hud_ = [self addProgressHUD];
-        [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
+        [hud_ setText:@"Reorganizing:\n\nWill Automatically\nClose When Done"];
         [self setStatusBarShowsProgress:YES];
 
         [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"];
@@ -8173,121 +8598,57 @@ static _finline void _setHomePage(Cydia *self) {
         return;
     }
 
-    if (Role_ == nil)
-        [self askForSettings];
-
     _trace();
-    overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
-
-    CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
-    book_ = [[CYBook alloc] initWithFrame:CGRectMake(
-        0, 0, screenrect.size.width, screenrect.size.height - 48
-    ) database:database_];
-
-    [book_ setDelegate:self];
-
-    [overlay_ addSubview:book_];
-
-    NSArray *buttonitems = [NSArray arrayWithObjects:
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"home-up.png", kUIButtonBarButtonInfo,
-            @"home-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            @"Cydia", kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"install-up.png", kUIButtonBarButtonInfo,
-            @"install-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            UCLocalize("SECTIONS"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"changes-up.png", kUIButtonBarButtonInfo,
-            @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            UCLocalize("CHANGES"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"manage-up.png", kUIButtonBarButtonInfo,
-            @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            UCLocalize("MANAGE"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"search-up.png", kUIButtonBarButtonInfo,
-            @"search-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            UCLocalize("SEARCH"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-    nil];
 
-    toolbar_ = [[UIToolbar alloc]
-        initInView:overlay_
-        withFrame:CGRectMake(
-            0, screenrect.size.height - ButtonBarHeight_,
-            screenrect.size.width, ButtonBarHeight_
-        )
-        withItemList:buttonitems
-    ];
+    NSMutableArray *items([NSMutableArray arrayWithObjects:
+        [[[UITabBarItem alloc] initWithTitle:@"Cydia" image:[UIImage applicationImageNamed:@"home.png"] tag:kCydiaTag] autorelease],
+        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SECTIONS") image:[UIImage applicationImageNamed:@"install.png"] tag:kSectionsTag] autorelease],
+        [[[UITabBarItem alloc] initWithTitle:UCLocalize("CHANGES") image:[UIImage applicationImageNamed:@"changes.png"] tag:kChangesTag] autorelease],
+        [[[UITabBarItem alloc] initWithTitle:UCLocalize("SEARCH") image:[UIImage applicationImageNamed:@"search.png"] tag:kSearchTag] autorelease],
+    nil]);
 
-    [toolbar_ setDelegate:self];
-    [toolbar_ setBarStyle:1];
-    [toolbar_ setButtonBarTrackingMode:2];
+    if (IsWildcat_) {
+        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:kSourcesTag] autorelease] atIndex:3];
+        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:kInstalledTag] autorelease] atIndex:3];
+    } else {
+        [items insertObject:[[[UITabBarItem alloc] initWithTitle:UCLocalize("MANAGE") image:[UIImage applicationImageNamed:@"manage.png"] tag:kManageTag] autorelease] atIndex:3];
+    }
 
-    int buttons[5] = {1, 2, 3, 4, 5};
-    [toolbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
-    [toolbar_ showButtonGroup:0 withDuration:0];
+    NSMutableArray *controllers([NSMutableArray array]);
 
-    for (int i = 0; i != 5; ++i)
-        [[toolbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
-            i * 64 + 2, 1, 60, ButtonBarHeight_
-        )];
+    for (UITabBarItem *item in items) {
+        CYNavigationController *controller([[[CYNavigationController alloc] initWithDatabase:database_] autorelease]);
+        [controller setTabBarItem:item];
+        [controllers addObject:controller];
+    }
 
-    [toolbar_ showSelectionForButton:1];
-    [overlay_ addSubview:toolbar_];
+    tabbar_ = [[CYTabBarController alloc] initWithDatabase:database_];
+    [tabbar_ setViewControllers:controllers];
+    [tabbar_ setDelegate:self];
+    [tabbar_ setSelectedIndex:0];
 
-    [UIKeyboard initImplementationNow];
-    CGSize keysize = [UIKeyboard defaultSize];
-    CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
-    keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
-    [overlay_ addSubview:keyboard_];
+    container_ = [[CYContainer alloc] initWithDatabase:database_];
+    [container_ setUpdateDelegate:self];
+    [container_ setTabBarController:tabbar_];
+    [window_ addSubview:[container_ view]];
 
-    [underlay_ addSubview:overlay_];
+    [self performSelector:@selector(loadData) withObject:nil afterDelay:0];
+}
 
-    [self reloadData];
+- (void) loadData {
+    if (Role_ == nil) {
+        [self showSettings];
+        return;
+    }
 
-    [self sectionsView];
-    changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
-    search_ = [[SearchView alloc] initWithBook:book_ database:database_];
+    [UIKeyboard initImplementationNow];
 
-    manage_ = (ManageView *) [[self
-        _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
-        withClass:[ManageView class]
-    ] retain];
+    [self reloadData];
 
 #if RecyclePackageViews
     details_ = [[NSMutableArray alloc] initWithCapacity:4];
-    [details_ addObject:[self _packageView]];
-    [details_ addObject:[self _packageView]];
+    [details_ addObject:[self _packageController]];
+    [details_ addObject:[self _packageController]];
 #endif
 
     PrintTimes();
@@ -8295,34 +8656,12 @@ static _finline void _setHomePage(Cydia *self) {
     _setHomePage(self);
 }
 
-- (void) showKeyboard:(BOOL)show {
-    CGSize keysize([UIKeyboard defaultSize]);
-    CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
-    CGRect keyup(keydown);
-    keyup.origin.y -= keysize.height;
-
-    UIFrameAnimation *animation([[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease]);
-    [animation setSignificantRectFields:2];
-
-    if (show) {
-        [animation setStartFrame:keydown];
-        [animation setEndFrame:keyup];
-        [keyboard_ activate];
+- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
+    if (item != nil && IsWildcat_) {
+        [sheet showFromBarButtonItem:item animated:YES];
     } else {
-        [animation setStartFrame:keyup];
-        [animation setEndFrame:keydown];
-        [keyboard_ deactivate];
+        [sheet showInView:window_];
     }
-
-    [[UIAnimator sharedAnimator]
-        addAnimations:[NSArray arrayWithObjects:animation, nil]
-        withDuration:KeyboardTime_
-        start:YES
-    ];
-}
-
-- (void) slideUp:(UIActionSheet *)alert {
-    [alert presentSheetInView:overlay_];
 }
 
 @end
@@ -8349,9 +8688,36 @@ MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL
     return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate);
 }
 
+static NSNumber *shouldPlayKeyboardSounds;
+
+Class $UIHardware;
+
+MSHook(void, UIHardware$_playSystemSound$, Class self, SEL _cmd, int sound) {
+    switch (sound) {
+        case 1104: // Keyboard Button Clicked
+        case 1105: // Keyboard Delete Repeated
+            if (shouldPlayKeyboardSounds == nil) {
+                NSDictionary *dict([[[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.preferences.sounds.plist"] autorelease]);
+                shouldPlayKeyboardSounds = [([dict objectForKey:@"keyboard"] ?: (id) kCFBooleanTrue) retain];
+            }
+
+            if (![shouldPlayKeyboardSounds boolValue])
+                break;
+
+        default:
+            _UIHardware$_playSystemSound$(self, _cmd, sound);
+    }
+}
+
 int main(int argc, char *argv[]) { _pooled
     _trace();
 
+    if (Class $UIDevice = objc_getClass("UIDevice")) {
+        UIDevice *device([$UIDevice currentDevice]);
+        IsWildcat_ = [device respondsToSelector:@selector(isWildcat)] && [device isWildcat];
+    } else
+        IsWildcat_ = false;
+
     PackageName = reinterpret_cast<CYString &(*)(Package *, SEL)>(method_getImplementation(class_getInstanceMethod([Package class], @selector(cyname))));
 
     /* Library Hacks {{{ */
@@ -8363,6 +8729,13 @@ int main(int argc, char *argv[]) { _pooled
         _UIWebDocumentView$_setUIKitDelegate$ = reinterpret_cast<void (*)(UIWebDocumentView *, SEL, id)>(method_getImplementation(UIWebDocumentView$_setUIKitDelegate$));
         method_setImplementation(UIWebDocumentView$_setUIKitDelegate$, reinterpret_cast<IMP>(&$UIWebDocumentView$_setUIKitDelegate$));
     }
+
+    $UIHardware = objc_getClass("UIHardware");
+    Method UIHardware$_playSystemSound$(class_getClassMethod($UIHardware, @selector(_playSystemSound:)));
+    if (UIHardware$_playSystemSound$ != NULL) {
+        _UIHardware$_playSystemSound$ = reinterpret_cast<void (*)(Class, SEL, int)>(method_getImplementation(UIHardware$_playSystemSound$));
+        method_setImplementation(UIHardware$_playSystemSound$, reinterpret_cast<IMP>(&$UIHardware$_playSystemSound$));
+    }
     /* }}} */
     /* Set Locale {{{ */
     Locale_ = CFLocaleCopyCurrent();
@@ -8498,6 +8871,8 @@ int main(int argc, char *argv[]) { _pooled
         Packages_ = [Metadata_ objectForKey:@"Packages"];
         Sections_ = [Metadata_ objectForKey:@"Sections"];
         Sources_ = [Metadata_ objectForKey:@"Sources"];
+
+        Token_ = [Metadata_ objectForKey:@"Token"];
     }
 
     if (Settings_ != nil)
@@ -8525,15 +8900,19 @@ int main(int argc, char *argv[]) { _pooled
 
     Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
 
+    if (substrate && access("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", F_OK) == 0)
+        dlopen("/Library/MobileSubstrate/DynamicLibraries/SimulatedKeyEvents.dylib", RTLD_LAZY | RTLD_GLOBAL);
     if (substrate && access("/Applications/WinterBoard.app/WinterBoard.dylib", F_OK) == 0)
         dlopen("/Applications/WinterBoard.app/WinterBoard.dylib", RTLD_LAZY | RTLD_GLOBAL);
     /*if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
         dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);*/
 
+    int version([[NSString stringWithContentsOfFile:@"/var/lib/cydia/firmware.ver"] intValue]);
+
     if (access("/tmp/.cydia.fw", F_OK) == 0) {
         unlink("/tmp/.cydia.fw");
         goto firmware;
-    } else if (access("/User", F_OK) != 0) {
+    } else if (access("/User", F_OK) != 0 || version < 2) {
       firmware:
         _trace();
         system("/usr/libexec/cydia/firmware.sh");
@@ -8584,7 +8963,8 @@ int main(int argc, char *argv[]) { _pooled
     if ($GSFontSetUseLegacyFontMetrics != NULL)
         $GSFontSetUseLegacyFontMetrics(YES);
 
-    UIKeyboardDisableAutomaticAppearance();
+    // XXX: I have a feeling this was important
+    //UIKeyboardDisableAutomaticAppearance();
     /* }}} */
 
     Colon_ = UCLocalize("COLON_DELIMITED");
@@ -8592,7 +8972,7 @@ int main(int argc, char *argv[]) { _pooled
     Warning_ = UCLocalize("WARNING");
 
     _trace();
-    int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
+    int value(UIApplicationMain(argc, argv, @"Cydia", @"Cydia"));
 
     CGColorSpaceRelease(space_);
     CFRelease(Locale_);