]> git.saurik.com Git - cydia.git/blobdiff - Cydia.mm
Use a static value for the width of the confirm prompt to prevent it from scrolling...
[cydia.git] / Cydia.mm
index ff3d34c56fa3dfa700c948258bf5f5842c9b8166..eb17d6a13ceed4f7565099b47dd606f64da0a6c7 100644 (file)
--- a/Cydia.mm
+++ b/Cydia.mm
@@ -1,7 +1,8 @@
 /* Cydia - iPhone UIKit Front-End for Debian APT
- * Copyright (C) 2008  Jay Freeman (saurik)
+ * Copyright (C) 2008-2010  Jay Freeman (saurik)
 */
 
+/* Modified BSD License {{{ */
 /*
  *        Redistribution and use in source and binary
  * forms, with or without modification, are permitted
  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
+/* }}} */
 
 // XXX: wtf/FastMalloc.h... wtf?
 #define USE_SYSTEM_MALLOC 1
 
 /* #include Directives {{{ */
-#import "UICaboodle.h"
+#import "UICaboodle/UCPlatform.h"
+#import "UICaboodle/UCLocalize.h"
 
-#include <objc/message.h>
 #include <objc/objc.h>
 #include <objc/runtime.h>
 
@@ -58,7 +60,6 @@
 #include <CoreFoundation/CFPriv.h>
 #include <CoreFoundation/CFUniChar.h>
 
-#import <QuartzCore/CALayer.h>
 #import <UIKit/UIKit.h>
 
 #include <WebCore/WebCoreThread.h>
@@ -71,6 +72,8 @@
 
 #include <ext/stdio_filebuf.h>
 
+#undef ABS
+
 #include <apt-pkg/acquire.h>
 #include <apt-pkg/acquire-item.h>
 #include <apt-pkg/algorithms.h>
@@ -87,6 +90,7 @@
 #include <apt-pkg/sourcelist.h>
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile.h>
 
 #include <apr-1/apr_pools.h>
 
@@ -112,25 +116,61 @@ extern "C" {
 
 #include <ext/hash_map>
 
-#import "BrowserView.h"
-#import "ResetView.h"
+#include <notify.h>
+
+#import "UICaboodle/BrowserView.h"
+#import "UICaboodle/ResetView.h"
 
 #import "substrate.h"
+
+// Apple's sample Reachability code, ASPL licensed.
+#import "Reachability.h"
 /* }}} */
 
-//#define _finline __attribute__((force_inline))
-#define _finline inline
+/* Header Fixes and Updates {{{ */
+typedef enum {
+   UIModalPresentationFullScreen = 0,
+   UIModalPresentationPageSheet,
+   UIModalPresentationFormSheet,
+   UIModalPresentationCurrentContext,
+} UIModalPresentationStyle;
+
+@interface UIAlertView (Private)
+- (void)setNumberOfRows:(int)rows;
+- (void)setContext:(id)context;
+- (id)context;
+@end
 
-struct timeval _ltv;
-bool _itv;
+@interface UIViewController (UIKit)
+- (id)navigationItem;
+- (id)navigationController;
+- (id)tabBarItem;
+@end
 
-#define _limit(count) do { \
-    static size_t _count(0); \
-    if (++_count == count) \
-        exit(0); \
-} while (false)
+@interface UITabBarController : UIViewController {
+    id _tabBar;
+    id _containerView;
+    id _viewControllerTransitionView;
+    id _viewControllers;
+    id _tabBarItemsToViewControllers;
+    id _selectedViewController;
+    id _moreNavigationController;
+    id _customizableViewControllers;
+    id _delegate;
+    id _selectedViewControllerDuringWillAppear;
+    id _transientViewController;
+    unsigned int isShowingMoreItem:1;
+    unsigned int needsToRebuildItems:1;
+    unsigned int isBarHidden:1;
+    unsigned int editButtonOnLeft:1;
+}
+@end
+/* }}} */
 
 /* Profiler {{{ */
+struct timeval _ltv;
+bool _itv;
+
 #define _timestamp ({ \
     struct timeval tv; \
     gettimeofday(&tv, NULL); \
@@ -196,48 +236,11 @@ void PrintTimes() {
 
 #define _end }
 /* }}} */
-/* Objective-C Handle<> {{{ */
-template <typename Type_>
-class _H {
-    typedef _H<Type_> This_;
-
-  private:
-    Type_ *value_;
-
-    _finline void Retain_() {
-        if (value_ != nil)
-            [value_ retain];
-    }
-
-    _finline void Clear_() {
-        if (value_ != nil)
-            [value_ release];
-    }
-
-  public:
-    _finline _H(Type_ *value = NULL, bool mended = false) :
-        value_(value)
-    {
-        if (!mended)
-            Retain_();
-    }
-
-    _finline ~_H() {
-        Clear_();
-    }
-
-    _finline This_ &operator =(Type_ *value) {
-        if (value_ != value) {
-            Clear_();
-            value_ = value;
-            Retain_();
-        } return this;
-    }
-};
-/* }}} */
 
 #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);
 }
@@ -246,6 +249,26 @@ void NSLogRect(const char *fix, const CGRect &rect) {
     NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
 }
 
+static _finline NSString *CydiaURL(NSString *path) {
+    char page[25];
+    page[0] = 'h'; page[1] = 't'; page[2] = 't'; page[3] = 'p'; page[4] = ':';
+    page[5] = '/'; page[6] = '/'; page[7] = 'c'; page[8] = 'y'; page[9] = 'd';
+    page[10] = 'i'; page[11] = 'a'; page[12] = '.'; page[13] = 's'; page[14] = 'a';
+    page[15] = 'u'; page[16] = 'r'; page[17] = 'i'; page[18] = 'k'; page[19] = '.';
+    page[20] = 'c'; page[21] = 'o'; page[22] = 'm'; page[23] = '/'; page[24] = '\0';
+    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;
 - (id) yieldToSelector:(SEL)selector;
@@ -310,29 +333,59 @@ void NSLogRect(const char *fix, const CGRect &rect) {
 }
 
 @end
+/* }}} */
 
-/* NSForcedOrderingSearch doesn't work on the iPhone */
-static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
-static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch;
-static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
+@interface CYActionSheet : UIAlertView {
+    unsigned button_;
+}
 
-/* iPhoneOS 2.0 Compatibility {{{ */
-#ifdef __OBJC2__
-@interface UITextView (iPhoneOS)
-- (void) setTextSize:(float)size;
+- (int) yieldToPopupAlertAnimated:(BOOL)animated;
 @end
 
-@implementation UITextView (iPhoneOS)
+@implementation CYActionSheet
+
+- (id) initWithTitle:(NSString *)title buttons:(NSArray *)buttons defaultButtonIndex:(int)index {
+    if ((self = [super init])) {
+        [self setTitle:title];
+        [self setDelegate:self];
+        for (NSString *button in buttons) [self addButtonWithTitle:button];
+        [self setCancelButtonIndex:index];
+    } return self;
+}
+
+- (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];
+}
 
-- (void) setTextSize:(float)size {
-    [self setFont:[[self font] fontWithSize:size]];
+- (int) yieldToPopupAlertAnimated:(BOOL)animated {
+    [self setRunsModal:YES];
+    button_ = 0;
+    [self show];
+    return button_;
 }
 
 @end
-#endif
-/* }}} */
 
-extern NSString * const kCAFilterNearest;
+/* NSForcedOrderingSearch doesn't work on the iPhone */
+static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch;
+static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch;
+static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
 
 /* Information Dictionaries {{{ */
 @interface NSMutableArray (Cydia)
@@ -354,41 +407,7 @@ extern NSString * const kCAFilterNearest;
 @implementation NSMutableDictionary (Cydia)
 
 - (void) addInfoDictionary:(NSDictionary *)info {
-    NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
-    [self setObject:info forKey:bundle];
-}
-
-@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];
+    [self setObject:info forKey:[info objectForKey:@"CFBundleIdentifier"]];
 }
 
 @end
@@ -403,10 +422,11 @@ extern NSString * const kCAFilterNearest;
 #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
+#define RecyclePackageViews (1 && ForRelease)
 #define AlwaysReload (1 && !ForRelease)
 
 #if !TraceLogging
@@ -548,7 +568,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);
     }
@@ -561,7 +581,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);
     }
@@ -640,14 +660,18 @@ void CFArrayInsertionSortValues(CFMutableArrayRef array, CFRange range, CFCompar
 @end
 /* }}} */
 
-typedef enum {
-    kUIControlEventMouseDown = 1 << 0,
-    kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
-    kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
-    kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
-    kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
-    kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
-} UIControlEventMasks;
+NSUInteger WebScriptObject$countByEnumeratingWithState$objects$count$(WebScriptObject *self, SEL sel, NSFastEnumerationState *state, id *objects, 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;
+}
 
 NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
     size_t length([self length] - state->state);
@@ -664,9 +688,9 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
 
 @interface NSString (UIKit)
 - (NSString *) stringByAddingPercentEscapes;
-- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
 @end
 
+/* Cydia NSString Additions {{{ */
 @interface NSString (Cydia)
 + (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length;
 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length withZone:(NSZone *)zone inPool:(apr_pool_t *)pool;
@@ -724,11 +748,8 @@ 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/"
     ];
 }
 
@@ -743,13 +764,9 @@ NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *s
 }
 
 @end
+/* }}} */
 
-static inline NSString *CYLocalizeEx(NSString *key, NSString *value = nil) {
-    return [[NSBundle mainBundle] localizedStringForKey:key value:value table:nil];
-}
-
-#define CYLocalize(key) CYLocalizeEx(@ key)
-
+/* C++ NSString Wrapper Cache {{{ */
 class CYString {
   private:
     char *data_;
@@ -757,8 +774,10 @@ class CYString {
     CFStringRef cache_;
 
     _finline void clear_() {
-        if (cache_ != nil)
+        if (cache_ != NULL) {
             CFRelease(cache_);
+            cache_ = NULL;
+        }
     }
 
   public:
@@ -782,7 +801,7 @@ class CYString {
     _finline CYString() :
         data_(0),
         size_(0),
-        cache_(nil)
+        cache_(NULL)
     {
     }
 
@@ -831,6 +850,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_;
     }
 
@@ -838,7 +859,8 @@ class CYString {
         return (NSString *) static_cast<CFStringRef>(*this);
     }
 };
-
+/* }}} */
+/* C++ NSString Algorithm Adapters {{{ */
 extern "C" {
     CF_EXPORT CFHashCode CFStringHashNSString(CFStringRef str);
 }
@@ -868,6 +890,7 @@ struct NSStringMapEqual :
         //[lhs isEqualToString:rhs];
     }
 };
+/* }}} */
 
 /* Perl-Compatible RegEx {{{ */
 class Pcre {
@@ -1029,15 +1052,16 @@ class CGColor {
 };
 /* }}} */
 
-extern "C" void UISetColor(CGColorRef color);
-
 /* Random Global Variables {{{ */
 static const int PulseInterval_ = 50000;
+static const int ButtonBarWidth_ = 60;
 static const int ButtonBarHeight_ = 48;
 static const float KeyboardTime_ = 0.3f;
 
+static int Finish_;
+static NSArray *Finishes_;
+
 #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
-#define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
 #define NotifyConfig_ "/etc/notify.conf"
 
 static bool Queuing_;
@@ -1057,10 +1081,8 @@ static UIColor *RemovingColor_;
 
 static NSString *App_;
 static NSString *Home_;
-static BOOL Sounds_Keyboard_;
 
 static BOOL Advanced_;
-static BOOL Loaded_;
 static BOOL Ignored_;
 
 static UIFont *Font12_;
@@ -1070,17 +1092,18 @@ 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 *Token_ = nil;
 static const NSString *UniqueID_ = nil;
 static const NSString *Build_ = nil;
 static const NSString *Product_ = nil;
 static const NSString *Safari_ = nil;
 
-CFLocaleRef Locale_;
-NSArray *Languages_;
-CGColorSpaceRef space_;
-
-bool bootstrap_;
-bool reload_;
+static CFLocaleRef Locale_;
+static NSArray *Languages_;
+static CGColorSpaceRef space_;
 
 static NSDictionary *SectionMap_;
 static NSMutableDictionary *Metadata_;
@@ -1092,24 +1115,13 @@ static _transient NSMutableDictionary *Sources_;
 static bool Changed_;
 static NSDate *now_;
 
+static bool IsWildcat_;
+
 #if RecycleWebViews
 static NSMutableArray *Documents_;
 #endif
-
-NSString *GetLastUpdate() {
-    NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
-
-    if (update == nil)
-        return CYLocalize("NEVER_OR_UNKNOWN");
-
-    CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
-    CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
-
-    CFRelease(formatter);
-
-    return [(NSString *) formatted autorelease];
-}
 /* }}} */
+
 /* Display Helpers {{{ */
 inline float Interpolate(float begin, float end, float fraction) {
     return (end - begin) * fraction + begin;
@@ -1132,18 +1144,24 @@ NSString *SizeString(double size) {
     return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
 }
 
-NSString *StripVersion(const char *version) {
+static _finline CFStringRef CFCString(const char *value) {
+    return CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(value), strlen(value), kCFStringEncodingUTF8, NO, kCFAllocatorNull);
+}
+
+const char *StripVersion_(const char *version) {
     const char *colon(strchr(version, ':'));
     if (colon != NULL)
         version = colon + 1;
-    return [NSString stringWithUTF8String:version];
+    return version;
 }
 
-NSString *StripVersion(NSString *version) {
-    NSRange colon = [version rangeOfString:@":"];
-    if (colon.location != NSNotFound)
-        version = [version substringFromIndex:(colon.location + 1)];
-    return version;
+CFStringRef StripVersion(const char *version) {
+    const char *colon(strchr(version, ':'));
+    if (colon != NULL)
+        version = colon + 1;
+    return CFStringCreateWithBytes(kCFAllocatorDefault, reinterpret_cast<const uint8_t *>(version), strlen(version), kCFStringEncodingUTF8, NO);
+    // XXX: performance
+    return CFCString(version);
 }
 
 NSString *LocalizeSection(NSString *section) {
@@ -1152,7 +1170,7 @@ NSString *LocalizeSection(NSString *section) {
         NSString *parent(title_r[1]);
         NSString *child(title_r[2]);
 
-        return [NSString stringWithFormat:CYLocalize("PARENTHETICAL"),
+        return [NSString stringWithFormat:UCLocalize("PARENTHETICAL"),
             LocalizeSection(parent),
             LocalizeSection(child)
         ];
@@ -1181,12 +1199,28 @@ NSString *Simplify(NSString *title) {
 }
 /* }}} */
 
+NSString *GetLastUpdate() {
+    NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
+
+    if (update == nil)
+        return UCLocalize("NEVER_OR_UNKNOWN");
+
+    CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
+    CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
+
+    CFRelease(formatter);
+
+    return [(NSString *) formatted autorelease];
+}
+
 bool isSectionVisible(NSString *section) {
     NSDictionary *metadata([Sections_ objectForKey:section]);
     NSNumber *hidden(metadata == nil ? nil : [metadata objectForKey:@"Hidden"]);
     return hidden == nil || ![hidden boolValue];
 }
 
+@class Cydia;
+
 /* Delegate Prototypes {{{ */
 @class Package;
 @class Source;
@@ -1194,19 +1228,8 @@ bool isSectionVisible(NSString *section) {
 @interface NSObject (ProgressDelegate)
 @end
 
-@implementation NSObject(ProgressDelegate)
-
-- (void) _setProgressError:(NSArray *)args {
-    [self performSelector:@selector(setProgressError:forPackage:)
-        withObject:[args objectAtIndex:0]
-        withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
-    ];
-}
-
-@end
-
 @protocol ProgressDelegate
-- (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
+- (void) setProgressError:(NSString *)error withTitle:(NSString *)id;
 - (void) setProgressTitle:(NSString *)title;
 - (void) setProgressPercent:(float)percent;
 - (void) startProgress;
@@ -1219,25 +1242,22 @@ 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) distUpgrade;
 - (void) updateData;
 - (void) syncData;
-- (void) askForSettings;
+- (void) showSettings;
 - (UIProgressHUD *) addProgressHUD;
 - (void) removeProgressHUD:(UIProgressHUD *)hud;
-- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
-- (RVPage *) pageForPackage:(NSString *)name;
-- (void) openMailToURL:(NSURL *)url;
-- (void) clearFirstResponder;
-- (PackageView *) packageView;
+- (UIViewController *) pageForPackage:(NSString *)name;
+- (PackageController *) packageController;
 @end
 /* }}} */
 
@@ -1258,6 +1278,10 @@ class Status :
         delegate_ = delegate;
     }
 
+    NSObject<ProgressDelegate> *getDelegate() const {
+        return delegate_;
+    }
+
     virtual bool MediaChange(std::string media, std::string drive) {
         return false;
     }
@@ -1267,7 +1291,7 @@ class Status :
 
     virtual void Fetch(pkgAcquire::ItemDesc &item) {
         //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
-        [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
+        [delegate_ setProgressTitle:[NSString stringWithFormat:UCLocalize("DOWNLOADING_"), [NSString stringWithUTF8String:item.ShortDesc.c_str()]]];
     }
 
     virtual void Done(pkgAcquire::ItemDesc &item) {
@@ -1288,7 +1312,7 @@ class Status :
         NSArray *fields([description componentsSeparatedByString:@" "]);
         NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
 
-        [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+        [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:)
             withObject:[NSArray arrayWithObjects:
                 [NSString stringWithUTF8String:error.c_str()],
                 source,
@@ -1323,16 +1347,23 @@ class Progress :
 {
   private:
     _transient id<ProgressDelegate> delegate_;
+    float percent_;
 
   protected:
     virtual void Update() {
+        /*if (abs(Percent - percent_) > 2)
+            //NSLog(@"%s:%s:%f", Op.c_str(), SubOp.c_str(), Percent);
+            percent_ = Percent;
+        }*/
+
         /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
         [delegate_ setProgressPercent:(Percent / 100)];*/
     }
 
   public:
     Progress() :
-        delegate_(nil)
+        delegate_(nil),
+        percent_(0)
     {
     }
 
@@ -1340,13 +1371,20 @@ class Progress :
         delegate_ = delegate;
     }
 
+    id getDelegate() const {
+        return delegate_;
+    }
+
     virtual void Done() {
+        //NSLog(@"DONE");
         //[delegate_ setProgressPercent:1];
     }
 };
 /* }}} */
 
 /* Database Interface {{{ */
+typedef std::map< unsigned long, _H<Source> > SourceMap;
+
 @interface Database : NSObject {
     NSZone *zone_;
     apr_pool_t *pool_;
@@ -1362,7 +1400,7 @@ class Progress :
     SPtr<pkgPackageManager> manager_;
     pkgSourceList *list_;
 
-    NSMutableDictionary *sources_;
+    SourceMap sources_;
     NSMutableArray *packages_;
 
     _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
@@ -1396,40 +1434,79 @@ class Progress :
 - (void) reloadData;
 
 - (void) configure;
-- (void) prepare;
+- (bool) prepare;
 - (void) perform;
-- (void) upgrade;
+- (bool) upgrade;
 - (void) update;
 
+- (void) setVisible;
+
 - (void) updateWithStatus:(Status &)status;
 
 - (void) setDelegate:(id)delegate;
 - (Source *) getSource:(pkgCache::PkgFileIterator)file;
+@end
+/* }}} */
+/* Delegate Helpers {{{ */
+@implementation NSObject(ProgressDelegate)
+
+- (void) _setProgressErrorPackage:(NSArray *)args {
+    [self performSelector:@selector(setProgressError:forPackage:)
+        withObject:[args objectAtIndex:0]
+        withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
+    ];
+}
+
+- (void) _setProgressErrorTitle:(NSArray *)args {
+    [self performSelector:@selector(setProgressError:withTitle:)
+        withObject:[args objectAtIndex:0]
+        withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
+    ];
+}
+
+- (void) _setProgressError:(NSString *)error withTitle:(NSString *)title {
+    [self performSelectorOnMainThread:@selector(_setProgressErrorTitle:)
+        withObject:[NSArray arrayWithObjects:error, title, nil]
+        waitUntilDone:YES
+    ];
+}
+
+- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
+    Package *package = id == nil ? nil : [[Database sharedInstance] packageWithName:id];
+    // XXX: holy typecast batman!
+    [self setProgressError:error withTitle:(package == nil ? id : [package name])];
+}
+
 @end
 /* }}} */
 
 /* Source Class {{{ */
 @interface Source : NSObject {
-    NSString *description_;
-    NSString *label_;
-    NSString *origin_;
-    NSString *support_;
+    CYString depiction_;
+    CYString description_;
+    CYString label_;
+    CYString origin_;
+    CYString support_;
 
-    NSString *uri_;
-    NSString *distribution_;
-    NSString *type_;
-    NSString *version_;
+    CYString uri_;
+    CYString distribution_;
+    CYString type_;
+    CYString version_;
+
+    NSString *host_;
+    NSString *authority_;
 
-    NSString *defaultIcon_;
+    CYString defaultIcon_;
 
     NSDictionary *record_;
     BOOL trusted_;
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index;
+- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool;
 
 - (NSComparisonResult) compareByNameAndType:(Source *)source;
 
+- (NSString *) depictionForPackage:(NSString *)package;
 - (NSString *) supportForPackage:(NSString *)package;
 
 - (NSDictionary *) record;
@@ -1453,23 +1530,33 @@ class Progress :
 
 @implementation Source
 
-#define _clear(field) \
-    if (field != nil) \
-        [field release]; \
-    field = nil;
-
 - (void) _clear {
-    _clear(uri_)
-    _clear(distribution_)
-    _clear(type_)
+    uri_.clear();
+    distribution_.clear();
+    type_.clear();
+
+    description_.clear();
+    label_.clear();
+    origin_.clear();
+    depiction_.clear();
+    support_.clear();
+    version_.clear();
+    defaultIcon_.clear();
+
+    if (record_ != nil) {
+        [record_ release];
+        record_ = nil;
+    }
+
+    if (host_ != nil) {
+        [host_ release];
+        host_ = nil;
+    }
 
-    _clear(description_)
-    _clear(label_)
-    _clear(origin_)
-    _clear(support_)
-    _clear(version_)
-    _clear(defaultIcon_)
-    _clear(record_)
+    if (authority_ != nil) {
+        [authority_ release];
+        authority_ = nil;
+    }
 }
 
 - (void) dealloc {
@@ -1489,52 +1576,72 @@ class Progress :
     return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
 }
 
-- (void) setMetaIndex:(metaIndex *)index {
+- (void) setMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
     [self _clear];
 
     trusted_ = index->IsTrusted();
 
-    uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
-    distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
-    type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
+    uri_.set(pool, index->GetURI());
+    distribution_.set(pool, index->GetDist());
+    type_.set(pool, index->GetType());
 
     debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
     if (dindex != NULL) {
-        std::ifstream release(dindex->MetaIndexFile("Release").c_str());
-        std::string line;
-        while (std::getline(release, line)) {
-            std::string::size_type colon(line.find(':'));
-            if (colon == std::string::npos)
-                continue;
-
-            std::string name(line.substr(0, colon));
-            std::string value(line.substr(colon + 1));
-            while (!value.empty() && value[0] == ' ')
-                value = value.substr(1);
-
-            if (name == "Default-Icon")
-                defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Description")
-                description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Label")
-                label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Origin")
-                origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Support")
-                support_ = [[NSString stringWithUTF8String:value.c_str()] retain];
-            else if (name == "Version")
-                version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
+        FileFd fd;
+        if (!fd.Open(dindex->MetaIndexFile("Release"), FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            pkgTagFile tags(&fd);
+
+            pkgTagSection section;
+            tags.Step(section);
+
+            struct {
+                const char *name_;
+                CYString *value_;
+            } names[] = {
+                {"default-icon", &defaultIcon_},
+                {"depiction", &depiction_},
+                {"description", &description_},
+                {"label", &label_},
+                {"origin", &origin_},
+                {"support", &support_},
+                {"version", &version_},
+            };
+
+            for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+                const char *start, *end;
+
+                if (section.Find(names[i].name_, start, end)) {
+                    CYString &value(*names[i].value_);
+                    value.set(pool, start, end - start);
+                }
+            }
         }
     }
 
     record_ = [Sources_ objectForKey:[self key]];
     if (record_ != nil)
         record_ = [record_ retain];
+
+    NSURL *url([NSURL URLWithString:uri_]);
+
+    host_ = [url host];
+    if (host_ != nil)
+        host_ = [[host_ lowercaseString] retain];
+
+    if (host_ != nil)
+        authority_ = host_;
+    else
+        authority_ = [url path];
+
+    if (authority_ != nil)
+        authority_ = [authority_ retain];
 }
 
-- (Source *) initWithMetaIndex:(metaIndex *)index {
+- (Source *) initWithMetaIndex:(metaIndex *)index inPool:(apr_pool_t *)pool {
     if ((self = [super init]) != nil) {
-        [self setMetaIndex:index];
+        [self setMetaIndex:index inPool:pool];
     } return self;
 }
 
@@ -1561,8 +1668,12 @@ class Progress :
     return [lhs compare:rhs options:LaxCompareOptions_];
 }
 
+- (NSString *) depictionForPackage:(NSString *)package {
+    return depiction_.empty() ? nil : [depiction_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+}
+
 - (NSString *) supportForPackage:(NSString *)package {
-    return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
+    return support_.empty() ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package];
 }
 
 - (NSDictionary *) record {
@@ -1586,15 +1697,15 @@ class Progress :
 }
 
 - (NSString *) key {
-    return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
+    return [NSString stringWithFormat:@"%@:%@:%@", (NSString *) type_, (NSString *) uri_, (NSString *) distribution_];
 }
 
 - (NSString *) host {
-    return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
+    return host_;
 }
 
 - (NSString *) name {
-    return origin_ == nil ? [self host] : origin_;
+    return origin_.empty() ? authority_ : origin_;
 }
 
 - (NSString *) description {
@@ -1602,7 +1713,7 @@ class Progress :
 }
 
 - (NSString *) label {
-    return label_ == nil ? [self host] : label_;
+    return label_.empty() ? authority_ : label_;
 }
 
 - (NSString *) origin {
@@ -1657,6 +1768,7 @@ class Progress :
 /* Package Class {{{ */
 @interface Package : NSObject {
     unsigned era_;
+    apr_pool_t *pool_;
 
     pkgCache::VerIterator version_;
     pkgCache::PkgIterator iterator_;
@@ -1665,14 +1777,17 @@ class Progress :
 
     Source *source_;
     bool cached_;
+    bool parsed_;
 
     CYString section_;
     NSString *section$_;
     bool essential_;
+    bool required_;
     bool visible_;
+    bool obsolete_;
 
     NSString *latest_;
-    NSString *installed_;
+    CYString installed_;
 
     CYString id_;
     CYString name_;
@@ -1687,8 +1802,9 @@ class Progress :
     CYString author_;
     Address *author$_;
 
+    CYString bugs_;
     CYString support_;
-    NSArray *tags_;
+    NSMutableArray *tags_;
     NSString *role_;
 
     NSArray *relationships_;
@@ -1703,6 +1819,7 @@ class Progress :
 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
 
 - (pkgCache::PkgIterator) iterator;
+- (void) parse;
 
 - (NSString *) section;
 - (NSString *) simpleSection;
@@ -1725,6 +1842,7 @@ class Progress :
 
 - (NSString *) latest;
 - (NSString *) installed;
+- (BOOL) uninstalled;
 
 - (BOOL) valid;
 - (BOOL) upgradableAndEssential:(BOOL)essential;
@@ -1739,6 +1857,8 @@ class Progress :
 - (BOOL) hasMode;
 - (NSString *) mode;
 
+- (void) setVisible;
+
 - (NSString *) id;
 - (NSString *) name;
 - (UIImage *) icon;
@@ -1774,8 +1894,9 @@ class Progress :
 - (void) remove;
 
 - (bool) isUnfilteredAndSearchedForBy:(NSString *)search;
+- (bool) isUnfilteredAndSelectedForBy:(NSString *)search;
 - (bool) isInstalledAndVisible:(NSNumber *)number;
-- (bool) isVisiblyUninstalledInSection:(NSString *)section;
+- (bool) isVisibleInSection:(NSString *)section;
 - (bool) isVisibleInSource:(Source *)source;
 
 @end
@@ -1872,6 +1993,11 @@ CFComparisonResult PackageNameCompare(Package *lhs, Package *rhs, void *arg) {
         CYString &rhi(PackageName(rhs, @selector(cyname)));
         CFStringRef lhn(lhi), rhn(rhi);
 
+        if (lhn == NULL)
+            return rhn == NULL ? NSOrderedSame : NSOrderedAscending;
+        else if (rhn == NULL)
+            return NSOrderedDescending;
+
         _profile(PackageNameCompare$NumbersLast)
             if (!lhi.empty() && !rhi.empty()) {
                 UniChar lhc(CFStringGetCharacterAtIndex(lhn, 0));
@@ -1916,8 +2042,6 @@ struct PackageNameOrdering :
 
     if (latest_ != nil)
         [latest_ release];
-    if (installed_ != nil)
-        [installed_ release];
 
     if (sponsor$_ != nil)
         [sponsor$_ release];
@@ -1959,100 +2083,135 @@ struct PackageNameOrdering :
     return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
 }
 
+- (void) parse {
+    if (parsed_)
+        return;
+    parsed_ = true;
+    if (file_.end())
+        return;
+
+    _profile(Package$parse)
+        pkgRecords::Parser *parser;
+
+        _profile(Package$parse$Lookup)
+            parser = &[database_ records]->Lookup(file_);
+        _end
+
+        CYString website;
+
+        _profile(Package$parse$Find)
+            struct {
+                const char *name_;
+                CYString *value_;
+            } names[] = {
+                {"icon", &icon_},
+                {"depiction", &depiction_},
+                {"homepage", &homepage_},
+                {"website", &website},
+                {"bugs", &bugs_},
+                {"support", &support_},
+                {"sponsor", &sponsor_},
+                {"author", &author_},
+            };
+
+            for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
+                const char *start, *end;
+
+                if (parser->Find(names[i].name_, start, end)) {
+                    CYString &value(*names[i].value_);
+                    _profile(Package$parse$Value)
+                        value.set(pool_, start, end - start);
+                    _end
+                }
+            }
+        _end
+
+        _profile(Package$parse$Tagline)
+            const char *start, *end;
+            if (parser->ShortDesc(start, end)) {
+                const char *stop(reinterpret_cast<const char *>(memchr(start, '\n', end - start)));
+                if (stop == NULL)
+                    stop = end;
+                while (stop != start && stop[-1] == '\r')
+                    --stop;
+                tagline_.set(pool_, start, stop - start);
+            }
+        _end
+
+        _profile(Package$parse$Retain)
+            if (homepage_.empty())
+                homepage_ = website;
+            if (homepage_ == depiction_)
+                homepage_.clear();
+        _end
+    _end
+}
+
+- (void) setVisible {
+    visible_ = required_ && [self unfiltered];
+}
+
 - (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
     if ((self = [super init]) != nil) {
     _profile(Package$initWithVersion)
     @synchronized (database) {
         era_ = [database era];
+        pool_ = pool;
 
         version_ = version;
         iterator_ = version.ParentPkg();
         database_ = database;
 
         _profile(Package$initWithVersion$Latest)
-            latest_ = [StripVersion(version_.VerStr()) retain];
+            latest_ = (NSString *) StripVersion(version_.VerStr());
         _end
 
-        pkgCache::VerIterator current(iterator_.CurrentVer());
-        if (!current.end())
-            installed_ = [StripVersion(current.VerStr()) retain];
+        pkgCache::VerIterator current;
+        _profile(Package$initWithVersion$Versions)
+            current = iterator_.CurrentVer();
+            if (!current.end())
+                installed_.set(pool_, StripVersion_(current.VerStr()));
 
-        if (!version_.end())
-            file_ = version_.FileList();
-        else {
-            pkgCache &cache([database_ cache]);
-            file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
-        }
+            if (!version_.end())
+                file_ = version_.FileList();
+            else {
+                pkgCache &cache([database_ cache]);
+                file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
+            }
+        _end
 
         _profile(Package$initWithVersion$Name)
-            id_.set(pool, iterator_.Name());
+            id_.set(pool_, iterator_.Name());
+            name_.set(pool, iterator_.Display());
         _end
 
-        if (!file_.end())
-            source_ = [database_ getSource:file_.File()];
-            if (source_ != nil)
-                [source_ retain];
-            cached_ = true;
-
-            _profile(Package$initWithVersion$Parse)
-                pkgRecords::Parser *parser;
-
-                _profile(Package$initWithVersion$Parse$Lookup)
-                    parser = &[database_ records]->Lookup(file_);
-                _end
-
-                CYString website;
-                CYString tag;
-
-                _profile(Package$initWithVersion$Parse$Find)
-                    struct {
-                        const char *name_;
-                        CYString *value_;
-                    } names[] = {
-                        {"name", &name_},
-                        {"icon", &icon_},
-                        {"depiction", &depiction_},
-                        {"homepage", &homepage_},
-                        {"website", &website},
-                        {"support", &support_},
-                        {"sponsor", &sponsor_},
-                        {"author", &author_},
-                        {"tag", &tag},
-                    };
-
-                    for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) {
-                        const char *start, *end;
-
-                        if (parser->Find(names[i].name_, start, end)) {
-                            CYString &value(*names[i].value_);
-                            _profile(Package$initWithVersion$Parse$Value)
-                                value.set(pool, start, end - start);
-                            _end
-                        }
-                    }
-                _end
-
-                _profile(Package$initWithVersion$Parse$Tagline)
-                    tagline_.set(pool, parser->ShortDesc());
-                _end
-
-                _profile(Package$initWithVersion$Parse$Retain)
-                    if (!homepage_.empty())
-                        homepage_ = website;
-                    if (homepage_ == depiction_)
-                        homepage_.clear();
-                    if (!tag.empty())
-                        tags_ = [[tag componentsSeparatedByString:@", "] retain];
-                _end
+        if (!file_.end()) {
+            _profile(Package$initWithVersion$Source)
+                source_ = [database_ getSource:file_.File()];
+                if (source_ != nil)
+                    [source_ retain];
+                cached_ = true;
             _end
+        }
+
+        required_ = true;
 
         _profile(Package$initWithVersion$Tags)
-            if (tags_ != nil)
-                for (NSString *tag in tags_)
-                    if ([tag hasPrefix:@"role::"]) {
-                        role_ = [[tag substringFromIndex:6] retain];
-                        break;
-                    }
+            pkgCache::TagIterator tag(iterator_.TagList());
+            if (!tag.end()) {
+                tags_ = [[NSMutableArray alloc] initWithCapacity:8];
+                do {
+                    const char *name(tag.Name());
+                    [tags_ addObject:(NSString *)CFCString(name)];
+                    if (role_ == nil && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/)
+                        role_ = (NSString *) CFCString(name + 6);
+                    if (required_ && strncmp(name, "require::", 9) == 0 && (
+                        true
+                    ))
+                        required_ = false;
+                    ++tag;
+                } while (!tag.end());
+            }
         _end
 
         bool changed(false);
@@ -2062,7 +2221,7 @@ struct PackageNameOrdering :
             metadata_ = [Packages_ objectForKey:key];
 
             if (metadata_ == nil) {
-                firstSeen_ = [now_ retain];
+                firstSeen_ = now_;
 
                 metadata_ = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
                     firstSeen_, @"FirstSeen",
@@ -2105,15 +2264,17 @@ struct PackageNameOrdering :
         _end
 
         _profile(Package$initWithVersion$Section)
-            section_.set(pool, iterator_.Section());
+            section_.set(pool_, iterator_.Section());
         _end
 
+        obsolete_ = [self hasTag:@"cydia::obsolete"];
         essential_ = ((iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES) || [self hasTag:@"cydia::essential"];
-        visible_ = [self hasSupportingRole] && [self unfiltered];
+        [self setVisible];
     } _end } return self;
 }
 
 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database {
+@synchronized ([Database class]) {
     pkgCache::VerIterator version;
 
     _profile(Package$packageWithIterator$GetCandidateVer)
@@ -2129,7 +2290,7 @@ struct PackageNameOrdering :
         inPool:pool
         database:database
     ] autorelease];
-}
+} }
 
 - (pkgCache::PkgIterator) iterator {
     return iterator_;
@@ -2162,7 +2323,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) longSection {
-    return LocalizeSection(section_);
+    return LocalizeSection([self section]);
 }
 
 - (NSString *) shortSection {
@@ -2260,6 +2421,10 @@ struct PackageNameOrdering :
     return installed_;
 }
 
+- (BOOL) uninstalled {
+    return installed_.empty();
+}
+
 - (BOOL) valid {
     return !version_.end();
 }
@@ -2284,7 +2449,7 @@ struct PackageNameOrdering :
 
 - (BOOL) unfiltered {
     NSString *section([self section]);
-    return section == nil || isSectionVisible(section);
+    return !obsolete_ && [self hasSupportingRole] && (section == nil || isSectionVisible(section));
 }
 
 - (BOOL) visible {
@@ -2337,11 +2502,9 @@ struct PackageNameOrdering :
                     return @"UPGRADE";
                 case 2:
                     return @"NEW_INSTALL";
-                default:
-                    _assert(false);
+                _nodefault
             }
-        default:
-            _assert(false);
+        _nodefault
     }
 }
 
@@ -2375,7 +2538,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) depiction {
-    return depiction_;
+    return !depiction_.empty() ? depiction_ : [[self source] depictionForPackage:id_];
 }
 
 - (Address *) sponsor {
@@ -2395,7 +2558,7 @@ struct PackageNameOrdering :
 }
 
 - (NSString *) support {
-    return !support_.empty() ? support_ : [[self source] supportForPackage:id_];
+    return !bugs_.empty() ? bugs_ : [[self source] supportForPackage:id_];
 }
 
 - (NSArray *) files {
@@ -2424,7 +2587,7 @@ struct PackageNameOrdering :
 
     size_t length(strlen(name));
     if (length < 2) invalid:
-        [warnings addObject:CYLocalize("ILLEGAL_PACKAGE_IDENTIFIER")];
+        [warnings addObject:UCLocalize("ILLEGAL_PACKAGE_IDENTIFIER")];
     else for (size_t i(0); i != length; ++i)
         if (
             /* XXX: technically this is not allowed */
@@ -2436,6 +2599,7 @@ struct PackageNameOrdering :
 
     if (strcmp(name, "cydia") != 0) {
         bool cydia = false;
+        bool user = false;
         bool _private = false;
         bool stash = false;
 
@@ -2445,6 +2609,8 @@ struct PackageNameOrdering :
             for (NSString *file in files)
                 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
                     cydia = true;
+                else if (!user && [file isEqualToString:@"/User"])
+                    user = true;
                 else if (!_private && [file isEqualToString:@"/private"])
                     _private = true;
                 else if (!stash && [file isEqualToString:@"/var/stash"])
@@ -2452,11 +2618,13 @@ struct PackageNameOrdering :
 
         /* XXX: this is not sensitive enough. only some folders are valid. */
         if (cydia && !repository)
-            [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"Cydia.app"]];
+            [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"Cydia.app"]];
+        if (user)
+            [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/User"]];
         if (_private)
-            [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"/private"]];
+            [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/private"]];
         if (stash)
-            [warnings addObject:[NSString stringWithFormat:CYLocalize("FILES_INSTALLED_TO"), @"/var/stash"]];
+            [warnings addObject:[NSString stringWithFormat:UCLocalize("FILES_INSTALLED_TO"), @"/var/stash"]];
     }
 
     return [warnings count] == 0 ? nil : warnings;
@@ -2663,16 +2831,34 @@ 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 installed] != nil;
+    return (![number boolValue] || [self visible]) && ![self uninstalled];
 }
 
-- (bool) isVisiblyUninstalledInSection:(NSString *)name {
+- (bool) isVisibleInSection:(NSString *)name {
     NSString *section = [self section];
 
     return
-        [self visible] &&
-        [self installed] == nil && (
+        [self visible] && (
             name == nil ||
             section == nil && [name length] == 0 ||
             [name isEqualToString:section]
@@ -2763,7 +2949,7 @@ struct PackageNameOrdering :
 /* XXX: localize the index thingees */
 - (Section *) initWithIndex:(unichar)index row:(size_t)row {
     if ((self = [super init]) != nil) {
-        name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain];
+        name_ = [[NSString stringWithCharacters:&index length:1] retain];
         index_ = index;
         row_ = row;
     } return self;
@@ -2804,8 +2990,9 @@ struct PackageNameOrdering :
 @end
 /* }}} */
 
-static int Finish_;
-static NSArray *Finishes_;
+static NSString *Colon_;
+static NSString *Error_;
+static NSString *Warning_;
 
 /* Database Implementation {{{ */
 @implementation Database
@@ -2849,7 +3036,7 @@ static NSArray *Finishes_;
         }
     }
 
-    _assert(false);
+    _assume(false);
 }
 
 - (void) _readStatus:(NSNumber *)fd { _pooled
@@ -2862,7 +3049,7 @@ static NSArray *Finishes_;
 
     while (std::getline(is, line)) {
         const char *data(line.c_str());
-        size_t size = line.size();
+        size_t size(line.size());
         lprintf("S:%s\n", data);
 
         if (conffile_r(data, size)) {
@@ -2880,7 +3067,7 @@ static NSArray *Finishes_;
             NSString *string = pmstatus_r[4];
 
             if (type == "pmerror")
-                [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+                [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:)
                     withObject:[NSArray arrayWithObjects:string, id, nil]
                     waitUntilDone:YES
                 ];
@@ -2888,11 +3075,13 @@ static NSArray *Finishes_;
                 [delegate_ setProgressTitle:string];
             } else if (type == "pmconffile")
                 [delegate_ setConfigurationData:string];
-            else _assert(false);
-        } else _assert(false);
+            else
+                lprintf("E:unknown pmstatus\n");
+        } else
+            lprintf("E:unknown status\n");
     }
 
-    _assert(false);
+    _assume(false);
 }
 
 - (void) _readOutput:(NSNumber *)fd { _pooled
@@ -2905,7 +3094,7 @@ static NSArray *Finishes_;
         [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
     }
 
-    _assert(false);
+    _assume(false);
 }
 
 - (FILE *) input {
@@ -2913,11 +3102,12 @@ static NSArray *Finishes_;
 }
 
 - (Package *) packageWithName:(NSString *)name {
+@synchronized ([Database class]) {
     if (static_cast<pkgDepCache *>(cache_) == NULL)
         return nil;
     pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
     return iterator.end() ? nil : [Package packageWithIterator:iterator withZone:NULL inPool:pool_ database:self];
-}
+} }
 
 - (Database *) init {
     if ((self = [super init]) != nil) {
@@ -2930,7 +3120,6 @@ static NSArray *Finishes_;
         zone_ = NSCreateZone(1024 * 1024, 256 * 1024, NO);
         apr_pool_create(&pool_, NULL);
 
-        sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
         packages_ = [[NSMutableArray alloc] init];
 
         int fds[2];
@@ -3003,7 +3192,10 @@ static NSArray *Finishes_;
 }
 
 - (NSArray *) sources {
-    return [sources_ allValues];
+    NSMutableArray *sources([NSMutableArray arrayWithCapacity:sources_.size()]);
+    for (SourceMap::const_iterator i(sources_.begin()); i != sources_.end(); ++i)
+        [sources addObject:i->second];
+    return sources;
 }
 
 - (NSArray *) issues {
@@ -3039,8 +3231,10 @@ static NSArray *Finishes_;
             [entry addObject:failure];
             [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
 
-            Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
-            [failure addObject:[package name]];
+            NSString *name([NSString stringWithUTF8String:start.TargetPkg().Name()]);
+            if (Package *package = [self packageWithName:name])
+                name = [package name];
+            [failure addObject:name];
 
             pkgCache::PkgIterator target(start.TargetPkg());
             if (target->ProvidesList != 0)
@@ -3070,11 +3264,47 @@ static NSArray *Finishes_;
     return issues;
 }
 
+- (bool) popErrorWithTitle:(NSString *)title {
+    bool fatal(false);
+    std::string message;
+
+    while (!_error->empty()) {
+        std::string error;
+        bool warning(!_error->PopMessage(error));
+        if (!warning)
+            fatal = true;
+        for (;;) {
+            size_t size(error.size());
+            if (size == 0 || error[size - 1] != '\n')
+                break;
+            error.resize(size - 1);
+        }
+        lprintf("%c:[%s]\n", warning ? 'W' : 'E', error.c_str());
+
+        if (!message.empty())
+            message += "\n\n";
+        message += error;
+    }
+
+    if (fatal && !message.empty())
+        [delegate_ _setProgressError:[NSString stringWithUTF8String:message.c_str()] withTitle:[NSString stringWithFormat:Colon_, fatal ? Error_ : Warning_, title]];
+
+    return fatal;
+}
+
+- (bool) popErrorWithTitle:(NSString *)title forOperation:(bool)success {
+    return [self popErrorWithTitle:title] || !success;
+}
+
 - (void) reloadData { _pooled
+@synchronized ([Database class]) {
     @synchronized (self) {
         ++era_;
     }
 
+    [packages_ removeAllObjects];
+    sources_.clear();
+
     _error->Discard();
 
     delete list_;
@@ -3091,6 +3321,11 @@ static NSArray *Finishes_;
     delete policy_;
     policy_ = NULL;
 
+    if (now_ != nil) {
+        [now_ release];
+        now_ = nil;
+    }
+
     cache_.Close();
 
     apr_pool_clear(pool_);
@@ -3100,12 +3335,12 @@ static NSArray *Finishes_;
     if (chk != -1)
         close(chk);
 
+    NSString *title(UCLocalize("DATABASE"));
+
     _trace();
-    if (!cache_.Open(progress_, true)) {
+    if (!cache_.Open(progress_, true)) { pop:
         std::string error;
-        if (!_error->PopMessage(error))
-            _assert(false);
-        _error->Discard();
+        bool warning(!_error->PopMessage(error));
         lprintf("cache_.Open():[%s]\n", error.c_str());
 
         if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
@@ -3115,8 +3350,12 @@ static NSArray *Finishes_;
         // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
         // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
         // else if (error == "The list of sources could not be read.")
-        else _assert(false);
+        else
+            [delegate_ _setProgressError:[NSString stringWithUTF8String:error.c_str()] withTitle:[NSString stringWithFormat:Colon_, warning ? Warning_ : Error_, title]];
 
+        if (warning)
+            goto pop;
+        _error->Discard();
         return;
     }
     _trace();
@@ -3132,35 +3371,43 @@ static NSArray *Finishes_;
     lock_ = NULL;
 
     list_ = new pkgSourceList();
-    _assert(list_->ReadMainList());
+    if ([self popErrorWithTitle:title forOperation:list_->ReadMainList()])
+        return;
+
+    if (cache_->DelCount() != 0 || cache_->InstCount() != 0) {
+        [delegate_ _setProgressError:@"COUNTS_NONZERO_EX" withTitle:title];
+        return;
+    }
 
-    _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
-    _assert(pkgApplyStatus(cache_));
+    if ([self popErrorWithTitle:title forOperation:pkgApplyStatus(cache_)])
+        return;
 
     if (cache_->BrokenCount() != 0) {
-        _assert(pkgFixBroken(cache_));
-        _assert(cache_->BrokenCount() == 0);
-        _assert(pkgMinimizeUpgrade(cache_));
+        if ([self popErrorWithTitle:title forOperation:pkgFixBroken(cache_)])
+            return;
+
+        if (cache_->BrokenCount() != 0) {
+            [delegate_ _setProgressError:@"STILL_BROKEN_EX" withTitle:title];
+            return;
+        }
+
+        if ([self popErrorWithTitle:title forOperation:pkgMinimizeUpgrade(cache_)])
+            return;
     }
 
     _trace();
-    {
-        std::string lists(_config->FindDir("Dir::State::lists"));
-
-        [sources_ removeAllObjects];
-        for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
-            std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
-            for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
-                if (debPackagesIndex *packages = dynamic_cast<debPackagesIndex *>(*index))
-                    [sources_
-                        setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
-                        forKey:[NSString stringWithFormat:@"%s%s",
-                            lists.c_str(),
-                            URItoFileName(packages->IndexURI("Packages")).c_str()
-                        ]
-                    ];
-        }
+
+    for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
+        std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
+        for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
+            // XXX: this could be more intelligent
+            if (dynamic_cast<debPackagesIndex *>(*index) != NULL) {
+                pkgCache::PkgFileIterator cached((*index)->FindInCache(cache_));
+                if (!cached.end())
+                    sources_[cached->ID] = [[[Source alloc] initWithMetaIndex:*source inPool:pool_] autorelease];
+            }
     }
+
     _trace();
 
     {
@@ -3169,8 +3416,6 @@ static NSArray *Finishes_;
         [packages_ release];
         packages_ = nil;*/
 
-        [packages_ removeAllObjects];
-
         _trace();
 
         for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
@@ -3208,20 +3453,25 @@ static NSArray *Finishes_;
 
         _trace();
     }
-}
+} }
 
 - (void) configure {
     NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
     system([dpkg UTF8String]);
 }
 
-- (void) clean {
+- (bool) clean {
+    // XXX: I don't remember this condition
     if (lock_ != NULL)
-        return;
+        return false;
 
     FileFd Lock;
     Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
-    _assert(!_error->PendingError());
+
+    NSString *title(UCLocalize("CLEAN_ARCHIVES"));
+
+    if ([self popErrorWithTitle:title])
+        return false;
 
     pkgAcquire fetcher;
     fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
@@ -3235,33 +3485,43 @@ static NSArray *Finishes_;
         }
     } cleaner;
 
-    if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
-        std::string error;
-        while (_error->PopMessage(error))
-            lprintf("ArchiveCleaner: %s\n", error.c_str());
-    }
+    if ([self popErrorWithTitle:title forOperation:cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)])
+        return false;
+
+    return true;
 }
 
-- (void) prepare {
+- (bool) prepare {
+    fetcher_->Shutdown();
+
     pkgRecords records(cache_);
 
     lock_ = new FileFd();
     lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
-    _assert(!_error->PendingError());
+
+    NSString *title(UCLocalize("PREPARE_ARCHIVES"));
+
+    if ([self popErrorWithTitle:title])
+        return false;
 
     pkgSourceList list;
-    // XXX: explain this with an error message
-    _assert(list.ReadMainList());
+    if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
+        return false;
 
     manager_ = (_system->CreatePM(cache_));
-    _assert(manager_->GetArchives(fetcher_, &list, &records));
-    _assert(!_error->PendingError());
+    if ([self popErrorWithTitle:title forOperation:manager_->GetArchives(fetcher_, &list, &records)])
+        return false;
+
+    return true;
 }
 
 - (void) perform {
+    NSString *title(UCLocalize("PERFORM_SELECTIONS"));
+
     NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
         pkgSourceList list;
-        _assert(list.ReadMainList());
+        if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
+            return;
         for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
             [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
     }
@@ -3275,6 +3535,8 @@ static NSArray *Finishes_;
     for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
         if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
             continue;
+        if ((*item)->Status == pkgAcquire::Item::StatIdle)
+            continue;
 
         std::string uri = (*item)->DescURI();
         std::string error = (*item)->ErrorText;
@@ -3282,7 +3544,7 @@ static NSArray *Finishes_;
         lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
         failed = true;
 
-        [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
+        [delegate_ performSelectorOnMainThread:@selector(_setProgressErrorPackage:)
             withObject:[NSArray arrayWithObjects:
                 [NSString stringWithUTF8String:error.c_str()],
             nil]
@@ -3315,7 +3577,8 @@ static NSArray *Finishes_;
 
     NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
         pkgSourceList list;
-        _assert(list.ReadMainList());
+        if ([self popErrorWithTitle:title forOperation:list.ReadMainList()])
+            return;
         for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
             [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
     }
@@ -3324,41 +3587,40 @@ static NSArray *Finishes_;
         [self update];
 }
 
-- (void) upgrade {
-    _assert(pkgDistUpgrade(cache_));
+- (bool) upgrade {
+    NSString *title(UCLocalize("UPGRADE"));
+    if ([self popErrorWithTitle:title forOperation:pkgDistUpgrade(cache_)])
+        return false;
+    return true;
 }
 
 - (void) update {
     [self updateWithStatus:status_];
 }
 
+- (void) setVisible {
+    for (Package *package in packages_)
+        [package setVisible];
+}
+
 - (void) updateWithStatus:(Status &)status {
+    _transient NSObject<ProgressDelegate> *delegate(status.getDelegate());
+    NSString *title(UCLocalize("REFRESHING_DATA"));
+
     pkgSourceList list;
-    _assert(list.ReadMainList());
+    if (!list.ReadMainList())
+        [delegate _setProgressError:@"Unable to read source list." withTitle:title];
 
     FileFd lock;
     lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
-    _assert(!_error->PendingError());
-
-    pkgAcquire fetcher(&status);
-    _assert(list.GetIndexes(&fetcher));
-
-    if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
-        bool failed = false;
-        for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
-            if ((*item)->Status != pkgAcquire::Item::StatDone) {
-                (*item)->Finished();
-                failed = true;
-            }
+    if ([self popErrorWithTitle:title])
+        return;
 
-        if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
-            _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
-            _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
-        }
+    if ([self popErrorWithTitle:title forOperation:ListUpdate(status, list, PulseInterval_)])
+        /* XXX: ignore this because users suck and don't understand why refreshing is important: return */;
 
-        [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
-        Changed_ = true;
-    }
+    [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
+    Changed_ = true;
 }
 
 - (void) setDelegate:(id)delegate {
@@ -3368,174 +3630,396 @@ static NSArray *Finishes_;
 }
 
 - (Source *) getSource:(pkgCache::PkgFileIterator)file {
-    if (const char *name = file.FileName())
-        if (Source *source = [sources_ objectForKey:[NSString stringWithUTF8String:name]])
-            return source;
-    return nil;
+    SourceMap::const_iterator i(sources_.find(file->ID));
+    return i == sources_.end() ? nil : i->second;
 }
 
 @end
 /* }}} */
 
-/* PopUp Windows {{{ */
-@interface PopUpView : UIView {
-    _transient id delegate_;
-    UITransitionView *transition_;
-    UIView *overlay_;
+/* Confirmation Controller {{{ */
+bool DepSubstrate(const pkgCache::VerIterator &iterator) {
+    if (!iterator.end())
+        for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
+            if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
+                continue;
+            pkgCache::PkgIterator package(dep.TargetPkg());
+            if (package.end())
+                continue;
+            if (strcmp(package.Name(), "mobilesubstrate") == 0)
+                return true;
+        }
+
+    return false;
 }
+/* }}} */
 
-- (void) cancel;
-- (id) initWithView:(UIView *)view delegate:(id)delegate;
+/* Web Scripting {{{ */
+@interface CydiaObject : NSObject {
+    id indirect_;
+    id delegate_;
+}
 
+- (id) initWithDelegate:(IndirectDelegate *)indirect;
 @end
 
-@implementation PopUpView
+@implementation CydiaObject
 
 - (void) dealloc {
-    [transition_ setDelegate:nil];
-    [transition_ release];
-    [overlay_ release];
+    [indirect_ release];
     [super dealloc];
 }
 
-- (void) cancel {
-    [transition_ transition:UITransitionPushFromTop toView:nil];
+- (id) initWithDelegate:(IndirectDelegate *)indirect {
+    if ((self = [super init]) != nil) {
+        indirect_ = [indirect retain];
+    } return self;
+}
+
+- (void) setDelegate:(id)delegate {
+    delegate_ = delegate;
 }
 
-- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
-    if (from != nil && to == nil)
-        [self removeFromSuperview];
++ (NSArray *) _attributeKeys {
+    return [NSArray arrayWithObjects:@"device", @"firewire", @"imei", @"mac", @"serial", nil];
 }
 
-- (id) initWithView:(UIView *)view delegate:(id)delegate {
-    if ((self = [super initWithFrame:[view bounds]]) != nil) {
-        delegate_ = delegate;
+- (NSArray *) attributeKeys {
+    return [[self class] _attributeKeys];
+}
+
++ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
+    return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
+}
 
-        transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
-        [self addSubview:transition_];
+- (NSString *) device {
+    return [[UIDevice currentDevice] uniqueIdentifier];
+}
 
-        overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
+#if 0 // XXX: implement!
+- (NSString *) mac {
+    if (![indirect_ promptForSensitive:@"Mac Address"])
+        return nil;
+}
 
-        [view addSubview:self];
+- (NSString *) serial {
+    if (![indirect_ promptForSensitive:@"Serial #"])
+        return nil;
+}
 
-        [transition_ setDelegate:self];
+- (NSString *) firewire {
+    if (![indirect_ promptForSensitive:@"Firewire GUID"])
+        return nil;
+}
 
-        UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
-        [transition_ transition:UITransitionNone toView:blank];
-        [transition_ transition:UITransitionPushFromBottom toView:overlay_];
-    } return self;
+- (NSString *) imei {
+    if (![indirect_ promptForSensitive:@"IMEI"])
+        return nil;
 }
+#endif
 
-@end
-/* }}} */
++ (NSString *) webScriptNameForSelector:(SEL)selector {
+    if (selector == @selector(close))
+        return @"close";
+    else if (selector == @selector(getInstalledPackages))
+        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:))
+        return @"setButtonImage";
+    else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
+        return @"setButtonTitle";
+    else if (selector == @selector(setFinishHook:))
+        return @"setFinishHook";
+    else if (selector == @selector(setPopupHook:))
+        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:))
+        return @"supports";
+    else if (selector == @selector(stringWithFormat:arguments:))
+        return @"format";
+    else if (selector == @selector(localizedStringForKey:value:table:))
+        return @"localize";
+    else if (selector == @selector(du:))
+        return @"du";
+    else if (selector == @selector(statfs:))
+        return @"statfs";
+    else
+        return nil;
+}
 
-#if 0
-/* Mail Composition {{{ */
-@interface MailToView : PopUpView {
-    MailComposeController *controller_;
++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
+    return [self webScriptNameForSelector:selector] == nil;
 }
 
-- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
+- (BOOL) supports:(NSString *)feature {
+    return [feature isEqualToString:@"window.open"];
+}
 
-@end
+- (NSArray *) getInstalledPackages {
+    NSArray *packages([[Database sharedInstance] packages]);
+    NSMutableArray *installed([NSMutableArray arrayWithCapacity:[packages count]]);
+    for (Package *package in packages)
+        if ([package installed] != nil)
+            [installed addObject:package];
+    return installed;
+}
 
-@implementation MailToView
+- (Package *) getPackageById:(NSString *)id {
+    Package *package([[Database sharedInstance] packageWithName:id]);
+    [package parse];
+    return package;
+}
 
-- (void) dealloc {
-    [controller_ release];
-    [super dealloc];
+- (NSArray *) statfs:(NSString *)path {
+    struct statfs stat;
+
+    if (path == nil || statfs([path UTF8String], &stat) == -1)
+        return nil;
+
+    return [NSArray arrayWithObjects:
+        [NSNumber numberWithUnsignedLong:stat.f_bsize],
+        [NSNumber numberWithUnsignedLong:stat.f_blocks],
+        [NSNumber numberWithUnsignedLong:stat.f_bfree],
+    nil];
 }
 
-- (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
-    NSLog(@"will");
+- (NSNumber *) du:(NSString *)path {
+    NSNumber *value(nil);
+
+    int fds[2];
+    _assert(pipe(fds) != -1);
+
+    pid_t pid(ExecFork());
+    if (pid == 0) {
+        _assert(dup2(fds[1], 1) != -1);
+        _assert(close(fds[0]) != -1);
+        _assert(close(fds[1]) != -1);
+        /* XXX: this should probably not use du */
+        execl("/usr/libexec/cydia/du", "du", "-s", [path UTF8String], NULL);
+        exit(1);
+        _assert(false);
+    }
+
+    _assert(close(fds[1]) != -1);
+
+    if (FILE *du = fdopen(fds[0], "r")) {
+        char line[1024];
+        while (fgets(line, sizeof(line), du) != NULL) {
+            size_t length(strlen(line));
+            while (length != 0 && line[length - 1] == '\n')
+                line[--length] = '\0';
+            if (char *tab = strchr(line, '\t')) {
+                *tab = '\0';
+                value = [NSNumber numberWithUnsignedLong:strtoul(line, NULL, 0)];
+            }
+        }
+
+        fclose(du);
+    } else _assert(close(fds[0]));
+
+    int status;
+  wait:
+    if (waitpid(pid, &status, 0) == -1)
+        if (errno == EINTR)
+            goto wait;
+        else _assert(false);
+
+    return value;
 }
 
-- (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
-    NSLog(@"did:%@", delivery);
-// [UIApp setStatusBarShowsProgress:NO];
-if ([controller error]){
-NSArray *buttons = [NSArray arrayWithObjects:CYLocalize("OK"), nil];
-UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:CYLocalize("ERROR") buttons:buttons defaultButtonIndex:0 delegate:self context:self];
-[mailAlertSheet setBodyText:[controller error]];
-[mailAlertSheet popupAlertAnimated:YES];
+- (void) close {
+    [indirect_ close];
 }
+
+- (void) installPackages:(NSArray *)packages {
+    [delegate_ performSelectorOnMainThread:@selector(installPackages:) withObject:packages waitUntilDone:NO];
 }
 
-- (void) showError {
-    NSLog(@"%@", [controller_ error]);
-    NSArray *buttons = [NSArray arrayWithObjects:CYLocalize("OK"), nil];
-    UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:CYLocalize("ERROR") buttons:buttons defaultButtonIndex:0 delegate:self context:self];
-    [mailAlertSheet setBodyText:[controller_ error]];
-    [mailAlertSheet popupAlertAnimated:YES];
+- (void) setAutoPopup:(BOOL)popup {
+    [indirect_ setAutoPopup:popup];
 }
 
-- (void) deliverMessage { _pooled
-    setuid(501);
-    setgid(501);
+- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
+    [indirect_ setButtonImage:button withStyle:style toFunction:function];
+}
 
-    if (![controller_ deliverMessage])
-        [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
+- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
+    [indirect_ setButtonTitle:button withStyle:style toFunction:function];
 }
 
-- (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
-    if ([controller_ needsDelivery])
-        [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
-    else
-        [self cancel];
+- (void) setSpecial:(id)function {
+    [indirect_ setSpecial:function];
 }
 
-- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
-    if ((self = [super initWithView:view delegate:delegate]) != nil) {
-        controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
-        [controller_ setDelegate:self];
-        [controller_ initializeUI];
-        [controller_ setupForURL:url];
+- (void) setToken:(NSString *)token {
+    if (Token_ != nil)
+        [Token_ release];
+    Token_ = [token retain];
 
-        UIView *view([controller_ view]);
-        [overlay_ addSubview:view];
-    } return self;
+    [Metadata_ setObject:Token_ forKey:@"Token"];
+    Changed_ = true;
+}
+
+- (void) setFinishHook:(id)function {
+    [indirect_ setFinishHook:function];
+}
+
+- (void) setPopupHook:(id)function {
+    [indirect_ setPopupHook:function];
+}
+
+- (void) setViewportWidth:(float)width {
+    [indirect_ setViewportWidth:width];
+}
+
+- (NSString *) stringWithFormat:(NSString *)format arguments:(WebScriptObject *)arguments {
+    //NSLog(@"SWF:\"%@\" A:%@", format, [arguments description]);
+    unsigned count([arguments count]);
+    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];
+}
+
+- (NSString *) localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)table {
+    if (reinterpret_cast<id>(value) == [WebUndefined undefined])
+        value = nil;
+    if (reinterpret_cast<id>(table) == [WebUndefined undefined])
+        table = nil;
+    return [[NSBundle mainBundle] localizedStringForKey:key value:value table:table];
 }
 
 @end
 /* }}} */
-#endif
 
-/* Confirmation View {{{ */
-bool DepSubstrate(const pkgCache::VerIterator &iterator) {
-    if (!iterator.end())
-        for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
-            if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
-                continue;
-            pkgCache::PkgIterator package(dep.TargetPkg());
-            if (package.end())
-                continue;
-            if (strcmp(package.Name(), "mobilesubstrate") == 0)
-                return true;
-        }
+/* Cydia View Controller {{{ */
+@interface CYViewController : UCViewController { }
+@end
 
-    return false;
+@implementation CYViewController
+@end
+/* }}} */
+
+@interface CYBrowserController : BrowserController {
+    CydiaObject *cydia_;
+}
+
+@end
+
+@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];
+
+    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"];
 }
 
-@protocol ConfirmationViewDelegate
-- (void) cancel;
-- (void) confirm;
+- (void) _setMoreHeaders:(NSMutableURLRequest *)request {
+    if (System_ != NULL)
+        [request setValue:System_ forHTTPHeaderField:@"X-System"];
+    if (Machine_ != NULL)
+        [request setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
+    if (Token_ != nil)
+        [request setValue:Token_ forHTTPHeaderField:@"X-Cydia-Token"];
+    if (Role_ != nil)
+        [request setValue:Role_ forHTTPHeaderField:@"X-Role"];
+}
+
+- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
+    NSMutableURLRequest *copy = [request mutableCopy];
+    [self _setMoreHeaders:copy];
+    return copy;
+}
+
+- (void) setDelegate:(id)delegate {
+    [super setDelegate:delegate];
+    [cydia_ setDelegate:delegate];
+}
+
+- (id) init {
+    if ((self = [super initWithWidth:[[self view] bounds].size.width ofClass:[CYBrowserController class]]) != nil) {
+        cydia_ = [[CydiaObject alloc] initWithDelegate:indirect_];
+
+        WebView *webview([document_ webView]);
+
+        Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
+
+        NSString *application = package == nil ? @"Cydia" : [NSString
+            stringWithFormat:@"Cydia/%@",
+            [package installed]
+        ];
+
+        if (Safari_ != nil)
+            application = [NSString stringWithFormat:@"Safari/%@ %@", Safari_, application];
+        if (Build_ != nil)
+            application = [NSString stringWithFormat:@"Mobile/%@ %@", Build_, application];
+        if (Product_ != nil)
+            application = [NSString stringWithFormat:@"Version/%@ %@", Product_, application];
+
+        [webview setApplicationNameForUserAgent:application];
+    } return self;
+}
+
+@end
+
+@protocol ConfirmationControllerDelegate
+- (void) cancelAndClear:(bool)clear;
+- (void) confirmWithNavigationController:(UINavigationController *)navigation;
 - (void) queue;
 @end
 
-@interface ConfirmationView : BrowserView {
+@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];
@@ -3547,34 +4031,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;
-            default:
-                _assert(false);
+        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 {
@@ -3582,12 +4064,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];
@@ -3626,34 +4111,28 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
         if (!remove)
             essential_ = nil;
-        else if (Advanced_ || true) {
-            NSString *parenthetical(CYLocalize("PARENTHETICAL"));
-
-            essential_ = [[UIActionSheet alloc]
-                initWithTitle:CYLocalize("REMOVING_ESSENTIALS")
-                buttons:[NSArray arrayWithObjects:
-                    [NSString stringWithFormat:parenthetical, CYLocalize("CANCEL_OPERATION"), CYLocalize("SAFE")],
-                    [NSString stringWithFormat:parenthetical, CYLocalize("FORCE_REMOVAL"), CYLocalize("UNSAFE")],
-                nil]
-                defaultButtonIndex:0
+        else if (Advanced_) {
+            NSString *parenthetical(UCLocalize("PARENTHETICAL"));
+
+            essential_ = [[UIAlertView alloc]
+                initWithTitle:UCLocalize("REMOVING_ESSENTIALS")
+                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
             ];
 
-#ifndef __OBJC2__
-            [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
-#endif
-            [essential_ setBodyText:CYLocalize("REMOVING_ESSENTIALS_EX")];
+            [essential_ setContext:@"remove"];
         } else {
-            essential_ = [[UIActionSheet alloc]
-                initWithTitle:CYLocalize("UNABLE_TO_COMPLY")
-                buttons:[NSArray arrayWithObjects:CYLocalize("OKAY"), nil]
-                defaultButtonIndex:0
+            essential_ = [[UIAlertView alloc]
+                initWithTitle:UCLocalize("UNABLE_TO_COMPLY")
+                message:UCLocalize("UNABLE_TO_COMPLY_EX")
                 delegate:self
-                context:@"unable"
+                cancelButtonTitle:UCLocalize("OKAY")
+                otherButtonTitles:nil
             ];
 
-            [essential_ setBodyText:CYLocalize("UNABLE_TO_COMPLY_EX")];
+            [essential_ setContext:@"unable"];
         }
 
         changes_ = [[NSArray alloc] initWithObjects:
@@ -3671,48 +4150,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 CYLocalize("CONFIRM");
-}
-
-- (NSString *) leftButtonTitle {
-    return [NSString stringWithFormat:CYLocalize("SLASH_DELIMITED"), CYLocalize("CANCEL"), CYLocalize("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 CYLocalize("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
@@ -3758,33 +4243,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_;
-    SHA1SumValue sandplate_;
+    NSString *title_;
 }
 
-- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
-
-- (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;
@@ -3794,175 +4269,139 @@ 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];
     [close_ release];
+    if (title_ != nil)
+        [title_ release];
     [super dealloc];
 }
 
-- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
-    if (bootstrap_ && from == overlay_ && to == view_)
-        exit(0);
-}
-
-- (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];
-
-        overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
-
-        if (bootstrap_)
-            [overlay_ setBackgroundColor:[UIColor blackColor]];
-        else {
-            background_ = [[UIView alloc] initWithFrame:[self bounds]];
-            [background_ setBackgroundColor:[UIColor blackColor]];
-            [self addSubview:background_];
-        }
-
-        [self addSubview:transition_];
+        [[self view] setBackgroundColor:(CGColor *)[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]];
 
-        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];
-        _trace();
 
-        output_ = [[UITextView alloc] initWithFrame:CGRectMake(
-            10,
-            navrect.size.height + 20,
-            bounds.size.width - 20,
-            bounds.size.height - navsize.height - 62 - navrect.size.height
-        )];
-        _trace();
+        output_ = [[UITextView alloc] init];
 
+        [output_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
         //[output_ setTextFont:@"Courier New"];
-        [output_ setTextSize:12];
-
+        [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_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
+        [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) resetView {
-    [transition_ transition:6 toView:view_];
-}
-
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
-
-    if ([context isEqualToString:@"error"])
-        [sheet dismiss];
-    else if ([context isEqualToString:@"conffile"]) {
+- (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) viewWillAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [[self navigationItem] setHidesBackButton:YES];
+    [[[self navigationController] navigationBar] setBarStyle:1];
+    
+    [self positionViews];
+}
+
+- (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;
-            default:
-                _assert(false);
-        }
-
-        [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:
-            [delegate_ suspendWithAnimation:YES];
+            [delegate_ terminateWithSuccess];
+            /*if ([delegate_ respondsToSelector:@selector(suspendWithAnimation:)])
+                [delegate_ suspendWithAnimation:YES];
+            else
+                [delegate_ suspend];*/
         break;
 
         case 2:
@@ -3980,96 +4419,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) _retachThread {
-    UINavigationItem *item = [navbar_ topItem];
-    [item setTitle:CYLocalize("COMPLETE")];
+    [[self navigationItem] setTitle:UCLocalize("COMPLETE")];
 
-    [overlay_ addSubview:close_];
+    [[self view] addSubview:close_];
     [progress_ removeFromSuperview];
     [status_ removeFromSuperview];
 
-    [delegate_ progressViewIsComplete:self];
-
-    if (Finish_ < 4) {
-        FileFd file(SandboxTemplate_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        if (!(sandplate_ == sha1.Result()))
-            Finish_ = 4;
-    }
+    [database_ popErrorWithTitle:title_];
+    [delegate_ progressControllerIsComplete:self];
 
     if (Finish_ < 4) {
-        FileFd file(NotifyConfig_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        if (!(notifyconf_ == sha1.Result()))
-            Finish_ = 4;
+        FileFd file;
+        if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            if (!(notifyconf_ == sha1.Result()))
+                Finish_ = 4;
+        }
     }
 
     if (Finish_ < 3) {
-        FileFd file(SpringBoard_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        if (!(springlist_ == sha1.Result()))
-            Finish_ = 3;
+        FileFd file;
+        if (!file.Open(SpringBoard_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            if (!(springlist_ == sha1.Result()))
+                Finish_ = 3;
+        }
     }
 
     switch (Finish_) {
-        case 0: [close_ setTitle:CYLocalize("RETURN_TO_CYDIA")]; break;
-        case 1: [close_ setTitle:CYLocalize("CLOSE_CYDIA")]; break;
-        case 2: [close_ setTitle:CYLocalize("RESTART_SPRINGBOARD")]; break;
-        case 3: [close_ setTitle:CYLocalize("RELOAD_SPRINGBOARD")]; break;
-        case 4: [close_ setTitle:CYLocalize("REBOOT_DEVICE")]; break;
-    }
-
-#define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
-
-    if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
-        [cache autorelease];
-
-        NSFileManager *manager = [NSFileManager defaultManager];
-        NSError *error = nil;
-
-        id system = [cache objectForKey:@"System"];
-        if (system == nil)
-            goto error;
-
-        struct stat info;
-        if (stat(Cache_, &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:@Cache_ atomically:YES];
-
-        if (chown(Cache_, info.st_uid, info.st_gid) == -1)
-            goto error;
-        if (chmod(Cache_, info.st_mode) == -1)
-            goto error;
-
-        if (false) error:
-            lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
+        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;
     }
 
-    notify_post("com.apple.mobile.application_installed");
+    system("su -c /usr/bin/uicache mobile");
+
+    UpdateExternalStatus(Finish_ == 0 ? 2 : 0);
 
     [delegate_ setStatusBarShowsProgress:NO];
 }
@@ -4082,46 +4477,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
-    UINavigationItem *item = [navbar_ topItem];
-    [item setTitle:title];
+    UpdateExternalStatus(1);
+
+    if (title_ != nil)
+        [title_ release];
+    if (title == nil)
+        title_ = nil;
+    else
+        title_ = [title retain];
+
+    [[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;
 
     {
-        FileFd file(SandboxTemplate_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        sandplate_ = sha1.Result();
-    }
-
-    {
-        FileFd file(NotifyConfig_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        notifyconf_ = sha1.Result();
+        FileFd file;
+        if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            notifyconf_ = sha1.Result();
+        }
     }
 
     {
-        FileFd file(SpringBoard_, FileFd::ReadOnly);
-        MMap mmap(file, MMap::ReadOnly);
-        SHA1Summation sha1;
-        sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
-        springlist_ = sha1.Result();
+        FileFd file;
+        if (!file.Open(SpringBoard_, FileFd::ReadOnly))
+            _error->Discard();
+        else {
+            MMap mmap(file, MMap::ReadOnly);
+            SHA1Summation sha1;
+            sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+            springlist_ = sha1.Result();
+        }
     }
 
-    [transition_ transition:6 toView:overlay_];
-
     [NSThread
         detachNewThreadSelector:@selector(_detachNewThreadData:)
         toTarget:self
@@ -4138,7 +4539,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         detachNewThreadSelector:selector
         toTarget:database_
         withObject:nil
-        title:CYLocalize("REPAIRING")
+        title:UCLocalize("REPAIRING")
     ];
 }
 
@@ -4150,19 +4551,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     ];
 }
 
-- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
-    Package *package = id == nil ? nil : [database_ packageWithName:id];
-
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:(package == nil ? id : [package name])
-        buttons:[NSArray arrayWithObjects:CYLocalize("OKAY"), nil]
+- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
+    CYActionSheet *sheet([[[CYActionSheet alloc]
+        initWithTitle:title
+        buttons:[NSArray arrayWithObjects:UCLocalize("OKAY"), nil]
         defaultButtonIndex:0
-        delegate:self
-        context:@"error"
-    ] autorelease];
+    ] autorelease]);
 
-    [sheet setBodyText:error];
-    [sheet popupAlertAnimated:YES];
+    [sheet setMessage:error];
+    [sheet yieldToPopupAlertAnimated:YES];
+    [sheet dismiss];
 }
 
 - (void) setProgressTitle:(NSString *)title {
@@ -4199,25 +4597,26 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) _setConfigurationData:(NSString *)data {
     static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
 
-    _assert(conffile_r(data));
+    if (!conffile_r(data)) {
+        lprintf("E:invalid conffile\n");
+        return;
+    }
 
     NSString *ofile = conffile_r[1];
     //NSString *nfile = conffile_r[2];
 
-    UIActionSheet *sheet = [[[UIActionSheet alloc]
-        initWithTitle:CYLocalize("CONFIGURATION_UPGRADE")
-        buttons:[NSArray arrayWithObjects:
-            CYLocalize("KEEP_OLD_COPY"),
-            CYLocalize("ACCEPT_NEW_COPY"),
-            // XXX: CYLocalize("SEE_WHAT_CHANGED"),
-        nil]
-        defaultButtonIndex:0
+    UIAlertView *alert = [[[UIAlertView alloc]
+        initWithTitle:UCLocalize("CONFIGURATION_UPGRADE")
+        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%@", CYLocalize("CONFIGURATION_UPGRADE_EX"), ofile]];
-    [sheet popupAlertAnimated:YES];
+    [alert setContext:@"conffile"];
+    [alert show];
 }
 
 - (void) _setProgressTitle:(NSString *)title {
@@ -4249,25 +4648,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
+/* Cell Content View {{{ */
+@interface ContentView : UIView {
+    _transient id delegate_;
+}
+
+@end
+
+@implementation ContentView
+- (id) initWithFrame:(CGRect)frame {
+    if ((self = [super initWithFrame:frame]) != nil) {
+        /* Fix landscape stretching. */
+        [self setNeedsDisplayOnBoundsChange:YES];
+    } return self;
+}
+
+- (void) setDelegate:(id)delegate {
+    delegate_ = delegate;
+}
+
+- (void) drawRect:(CGRect)rect {
+    [super drawRect:rect];
+    [delegate_ drawContentRect:rect];
+}
+@end
+/* }}} */
 /* Package Cell {{{ */
-@interface PackageCell : UITableCell {
+@interface PackageCell : UITableViewCell {
     UIImage *icon_;
     NSString *name_;
     NSString *description_;
     bool commercial_;
     NSString *source_;
     UIImage *badge_;
-    bool cached_;
     Package *package_;
-#ifdef USE_BADGES
-    UITextLabel *status_;
-#endif
+    UIColor *color_;
+    ContentView *content_;
+    BOOL faded_;
+    float fade_;
+    UIImage *placard_;
 }
 
 - (PackageCell *) init;
 - (void) setPackage:(Package *)package;
 
 + (int) heightForPackage:(Package *)package;
+- (void) drawContentRect:(CGRect)rect;
 
 @end
 
@@ -4299,36 +4725,71 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         badge_ = nil;
     }
 
+    if (placard_ != nil) {
+        [placard_ release];
+        placard_ = nil;
+    }
+
     [package_ release];
     package_ = nil;
 }
 
 - (void) dealloc {
     [self clearPackage];
-#ifdef USE_BADGES
-    [status_ release];
-#endif
+    [content_ release];
+    [color_ release];
     [super dealloc];
 }
 
+- (float) fade {
+    return faded_ ? [self selectionPercent] : fade_;
+}
+
 - (PackageCell *) init {
-    if ((self = [super init]) != nil) {
-#ifdef USE_BADGES
-        status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
-        [status_ setBackgroundColor:[UIColor clearColor]];
-        [status_ setFont:small];
-#endif
+    CGRect frame(CGRectMake(0, 0, 320, 74));
+    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_ setOpaque:YES];
+        if ([self respondsToSelector:@selector(selectionPercent)])
+            faded_ = YES;
     } return self;
 }
 
+- (void) _setBackgroundColor {
+    UIColor *color;
+    if (NSString *mode = [package_ mode]) {
+        bool remove([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]);
+        color = remove ? RemovingColor_ : InstallingColor_;
+    } else
+        color = [UIColor whiteColor];
+
+    [content_ setBackgroundColor:color];
+    [self setNeedsDisplay];
+}
+
 - (void) setPackage:(Package *)package {
     [self clearPackage];
+    [package parse];
 
     Source *source = [package source];
 
     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];
@@ -4340,76 +4801,43 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         label = [source label];
         trusted = [source trusted];
     } else if ([[package id] isEqualToString:@"firmware"])
-        label = CYLocalize("APPLE");
+        label = UCLocalize("APPLE");
     else
-        label = [NSString stringWithFormat:CYLocalize("SLASH_DELIMITED"), CYLocalize("UNKNOWN"), CYLocalize("LOCAL")];
+        label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")];
 
     NSString *from(label);
 
     NSString *section = [package simpleSection];
     if (section != nil && ![section isEqualToString:label]) {
         section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
-        from = [NSString stringWithFormat:CYLocalize("PARENTHETICAL"), from, section];
+        from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section];
     }
 
-    from = [NSString stringWithFormat:CYLocalize("FROM"), from];
+    from = [NSString stringWithFormat:UCLocalize("FROM"), from];
     source_ = [from retain];
 
     if (NSString *purpose = [package primaryPurpose])
         if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
             badge_ = [badge_ retain];
 
-#ifdef USE_BADGES
-    if (NSString *mode = [package mode]) {
-        [badge_ setImage:[UIImage applicationImageNamed:
-            [mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"] ? @"removing.png" : @"installing.png"
-        ]];
-
-        [status_ setText:[NSString stringWithFormat:CYLocalize("QUEUED_FOR"), CYLocalize(mode)]];
-        [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
-    } else if ([package half]) {
-        [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
-        [status_ setText:CYLocalize("PACKAGE_DAMAGED")];
-        [status_ setColor:[UIColor redColor]];
-    } else {
-        [badge_ setImage:nil];
-        [status_ setText:nil];
-    }
-#endif
-
-    cached_ = false;
-}
-
-- (void) drawRect:(CGRect)rect {
-    if (!cached_) {
-        UIColor *color;
-
-        if (NSString *mode = [package_ mode]) {
-            bool remove([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]);
-            color = remove ? RemovingColor_ : InstallingColor_;
-        } else
-            color = [UIColor whiteColor];
-
-        [self setBackgroundColor:color];
-        cached_ = true;
-    }
+    if ([package installed] != nil)
+        if ((placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/installed.png", App_]]) != nil)
+            placard_ = [placard_ retain];
 
-    [super drawRect:rect];
+    [self _setBackgroundColor];
+    [content_ setNeedsDisplay];
 }
 
-- (void) drawBackgroundInRect:(CGRect)rect withFade:(float)fade {
-    if (fade == 0) {
-        CGContextRef context(UIGraphicsGetCurrentContext());
-        [[self backgroundColor] set];
-        CGRect back(rect);
-        back.size.height -= 1;
-        CGContextFillRect(context, back);
-    }
+- (void) drawContentRect:(CGRect)rect {
+    bool selected([self isSelected]);
+    float width([self bounds].size.width);
 
-    [super drawBackgroundInRect:rect withFade:fade];
-}
+#if 0
+    CGContextRef context(UIGraphicsGetCurrentContext());
+    [([[self selectedBackgroundView] superview] != nil ? [UIColor clearColor] : [self backgroundColor]) set];
+    CGContextFillRect(context, rect);
+#endif
 
-- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
     if (icon_ != nil) {
         CGRect rect;
         rect.size = [icon_ size];
@@ -4437,41 +4865,38 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     if (!selected)
         UISetColor(commercial_ ? Purple_ : Black_);
-    [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 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_ ellipsis:2];
+    [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ ellipsis:2];
 
     if (!selected)
         UISetColor(commercial_ ? Purplish_ : Gray_);
-    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
+    [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ ellipsis:2];
 
-    [super drawContentInRect:rect selected:selected];
+    if (placard_ != nil)
+        [placard_ drawAtPoint:CGPointMake(width - 52, 9)];
 }
 
-- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
-    cached_ = false;
-    [super setSelected:selected withFade:fade];
+- (void) setSelected:(BOOL)selected animated:(BOOL)fade {
+    //[self _setBackgroundColor];
+    [super setSelected:selected animated:fade];
+    [content_ setNeedsDisplay];
 }
 
 + (int) heightForPackage:(Package *)package {
-    NSString *tagline([package shortDescription]);
-    int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
-#ifdef USE_BADGES
-    if ([package hasMode] || [package half])
-        return height + 96;
-    else
-#endif
-        return height + 73;
+    return 73;
 }
 
 @end
 /* }}} */
 /* Section Cell {{{ */
-@interface SectionCell : UISimpleTableCell {
+@interface SectionCell : UITableViewCell {
+    NSString *basic_;
     NSString *section_;
     NSString *name_;
     NSString *count_;
     UIImage *icon_;
-    _UISwitchSlider *switch_;
+    ContentView *content_;
+    id switch_;
     BOOL editing_;
 }
 
@@ -4483,6 +4908,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @implementation SectionCell
 
 - (void) clearSection {
+    if (basic_ != nil) {
+        [basic_ release];
+        basic_ = nil;
+    }
+
     if (section_ != nil) {
         [section_ release];
         section_ = nil;
@@ -4503,27 +4933,38 @@ 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];
 
-        switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
-        [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
+        UIView *content([self contentView]);
+        CGRect bounds([content bounds]);
+
+        content_ = [[ContentView alloc] initWithFrame:bounds];
+        [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [content addSubview:content_];
+        [content_ setBackgroundColor:[UIColor whiteColor]];
+
+        [content_ setDelegate:self];
     } return self;
 }
 
 - (void) onSwitch:(id)sender {
-    NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
+    NSMutableDictionary *metadata = [Sections_ objectForKey:basic_];
     if (metadata == nil) {
         metadata = [NSMutableDictionary dictionaryWithCapacity:2];
-        [Sections_ setObject:metadata forKey:section_];
+        [Sections_ setObject:metadata forKey:basic_];
     }
 
     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 {
@@ -4538,21 +4979,38 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self clearSection];
 
     if (section == nil) {
-        name_ = [CYLocalize("ALL_PACKAGES") retain];
+        name_ = [UCLocalize("ALL_PACKAGES") retain];
         count_ = nil;
     } else {
+        basic_ = [section name];
+        if (basic_ != nil)
+            basic_ = [basic_ retain];
+
         section_ = [section localized];
         if (section_ != nil)
             section_ = [section_ retain];
-        name_  = [(section_ == nil ? CYLocalize("NO_SECTION") : section_) retain];
+
+        name_  = [(section_ == nil || [section_ length] == 0 ? UCLocalize("NO_SECTION") : section_) retain];
         count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
 
         if (editing_)
-            [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
+            [switch_ setOn:(isSectionVisible(basic_) ? 1 : 0) animated:NO];
     }
+
+    [self setAccessoryType:editing ? 0 : 1 /*UITableViewCellAccessoryDisclosureIndicator*/];
+    [content_ setNeedsDisplay];
+}
+
+- (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) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
+- (void) drawContentRect:(CGRect)rect {
+    BOOL selected = [self isSelected];
+    
     [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
 
     if (selected)
@@ -4560,30 +5018,33 @@ 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_ ellipsis:2];
 
     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 {
     _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
@@ -4600,48 +5061,43 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (int) numberOfRowsInTable:(UITable *)table {
+- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)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;
+}*/
 
-- (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 *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    static NSString *reuseIdentifier = @"Cell";
+
+    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:0 /*UITableViewCellSelectionStyleNone*/];
 
-- (BOOL) table:(UITable *)table canSelectRow:(int)row {
-    return NO;
+    return cell;
 }
 
-- (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:CYLocalize("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;
 }
 
@@ -4690,27 +5146,14 @@ 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 CYLocalize("INSTALLED_FILES");
-}
-
-- (NSString *) backButtonTitle {
-    return CYLocalize("FILES");
 }
 
 @end
 /* }}} */
-/* Package View {{{ */
-@interface PackageView : BrowserView {
+/* Package Controller {{{ */
+@interface PackageController : CYBrowserController {
     _transient Database *database_;
     Package *package_;
     NSString *name_;
@@ -4718,12 +5161,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)
@@ -4736,39 +5179,38 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) release {
     if ([self retainCount] == 1)
-        [delegate_ setPackageView:self];
+        [delegate_ setPackageController:self];
     [super release];
 }
 
 /* XXX: this is not safe at all... localization of /fail/ */
 - (void) _clickButtonWithName:(NSString *)name {
-    if ([name isEqualToString:CYLocalize("CLEAR")])
+    if ([name isEqualToString:UCLocalize("CLEAR")])
         [delegate_ clearPackage:package_];
-    else if ([name isEqualToString:CYLocalize("INSTALL")])
+    else if ([name isEqualToString:UCLocalize("INSTALL")])
         [delegate_ installPackage:package_];
-    else if ([name isEqualToString:CYLocalize("REINSTALL")])
+    else if ([name isEqualToString:UCLocalize("REINSTALL")])
         [delegate_ installPackage:package_];
-    else if ([name isEqualToString:CYLocalize("REMOVE")])
+    else if ([name isEqualToString:UCLocalize("REMOVE")])
         [delegate_ removePackage:package_];
-    else if ([name isEqualToString:CYLocalize("UPGRADE")])
+    else if ([name isEqualToString:UCLocalize("UPGRADE")])
         [delegate_ installPackage:package_];
     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)]];
-
-        [sheet dismiss];
-    } else
-        [super alertSheet:sheet buttonClicked:button];
+        if (button != [sheet cancelButtonIndex]) {
+            NSString *buttonName = [buttons_ objectAtIndex:button];
+            [self _clickButtonWithName:buttonName];
+        }
+        
+        [sheet dismissWithClickedButtonIndex:-1 animated:YES];
+    } else {
+        [super alertSheet:sheet clickedButtonAtIndex:button];
+    }
 }
 
 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
@@ -4785,46 +5227,53 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 #if !AlwaysReload
-- (void) __rightButtonClicked {
-    int count = [buttons_ count];
-    _assert(count != 0);
+- (void) _actionButtonClicked {
+    int count([buttons_ count]);
+    if (count == 0)
+        return;
 
     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:CYLocalize("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 {
-    if (commercial_)
-        [super _rightButtonClicked];
-    else
-        [self __rightButtonClicked];
+- (void) actionButtonClicked {
+       // Wait until it's done loading.
+    if (![self isLoading])
+        [self _actionButtonClicked];
 }
-#endif
 
-- (id) _rightButtonTitle {
-    int count = [buttons_ count];
-    return count == 0 ? nil : count != 1 ? CYLocalize("MODIFY") : [buttons_ objectAtIndex:0];
+- (void) reloadButtonClicked {
+    // Don't reload a package view by clicking the button.
 }
 
-- (NSString *) backButtonTitle {
-    return @"Details";
+- (void) applyLoadingTitle {
+    // Don't show "Loading" as the title. Ever.
 }
+#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"]]];
@@ -4845,32 +5294,36 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [buttons_ removeAllObjects];
 
     if (package != nil) {
+        [package parse];
+
         package_ = [package retain];
         name_ = [[package id] retain];
         commercial_ = [package isCommercial];
 
         if ([package_ mode] != nil)
-            [buttons_ addObject:CYLocalize("CLEAR")];
+            [buttons_ addObject:UCLocalize("CLEAR")];
         if ([package_ source] == nil);
         else if ([package_ upgradableAndEssential:NO])
-            [buttons_ addObject:CYLocalize("UPGRADE")];
-        else if ([package_ installed] == nil)
-            [buttons_ addObject:CYLocalize("INSTALL")];
+            [buttons_ addObject:UCLocalize("UPGRADE")];
+        else if ([package_ uninstalled])
+            [buttons_ addObject:UCLocalize("INSTALL")];
         else
-            [buttons_ addObject:CYLocalize("REINSTALL")];
-        if ([package_ installed] != nil)
-            [buttons_ addObject:CYLocalize("REMOVE")];
+            [buttons_ addObject:UCLocalize("REINSTALL")];
+        if (![package_ uninstalled])
+            [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];
 
@@ -4882,8 +5335,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             [super callFunction:special_];
         }
     }
+}
 
-    [self reloadButtons];
+- (void) applyRightButton {
+    int count = [buttons_ count];
+    UIBarButtonItem *actionItem = [[UIBarButtonItem alloc]
+        initWithTitle:count == 0 ? nil : count != 1 ? UCLocalize("MODIFY") : [buttons_ objectAtIndex:0]
+        style:UIBarButtonItemStylePlain
+        target:self
+        action:@selector(actionButtonClicked)
+    ];
+    if (![self isLoading]) [[self navigationItem] setRightBarButtonItem:actionItem];
+       else [super applyRightButton];
+    [actionItem release];
 }
 
 - (bool) isLoading {
@@ -4897,110 +5361,120 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Package Table {{{ */
-@interface PackageTable : RVPage {
+@interface PackageTable : UIView {
     _transient Database *database_;
-    NSString *title_;
     NSMutableArray *packages_;
     NSMutableArray *sections_;
-    UISectionList *list_;
+    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;
 
 - (void) reloadData;
 - (void) resetCursor;
 
-- (UISectionList *) list;
+- (UITableView *) list;
 
 - (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];
 }
 
-- (int) numberOfSectionsInSectionList:(UISectionList *)list {
-    return [sections_ count];
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
+    NSInteger count([sections_ count]);
+    return count == 0 ? 1 : count;
 }
 
-- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
+- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
+    if ([sections_ count] == 0)
+        return nil;
     return [[sections_ objectAtIndex:section] name];
 }
 
-- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
-    return [[sections_ objectAtIndex:section] row];
+- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
+    if ([sections_ count] == 0)
+        return 0;
+    return [[sections_ objectAtIndex:section] count];
 }
 
-- (int) numberOfRowsInTable:(UITable *)table {
-    return [packages_ count];
+- (Package *) packageAtIndexPath:(NSIndexPath *)path {
+    Section *section([sections_ objectAtIndex:[path section]]);
+    NSInteger row([path row]);
+    Package *package([packages_ objectAtIndex:([section row] + row)]);
+    return package;
 }
 
-- (float) table:(UITable *)table heightForRow:(int)row {
-    return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
+- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
+    PackageCell *cell([table dequeueReusableCellWithIdentifier:@"Package"]);
+    if (cell == nil)
+        cell = [[[PackageCell alloc] init] autorelease];
+    [cell setPackage:[self packageAtIndexPath:path]];
+    return cell;
 }
 
-- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
-    if (reusing == nil)
-        reusing = [[[PackageCell alloc] init] autorelease];
-    [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
-    return reusing;
+- (void) deselectWithAnimation:(BOOL)animated {
+    [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
 }
 
-- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
-    return NO;
+/*- (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]];
+    [target_ performSelector:action_ withObject:package];
+    return path;
 }
 
-- (void) tableRowSelected:(NSNotification *)notification {
-    int row = [[notification object] selectedRow];
-    if (row == INT_MAX)
-        return;
+- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView {
+    return [packages_ count] > 20 ? index_ : nil;
+}
 
-    Package *package = [packages_ objectAtIndex:row];
-    package = [database_ packageWithName:[package id]];
-    PackageView *view([delegate_ packageView]);
-    [view setPackage:package];
-    [view setDelegate:delegate_];
-    [book_ pushPage:view];
+- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
+    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];
 
-        packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
-        sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
-
-        list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
-        [list_ setDataSource:self];
+        target_ = target;
+        action_ = action;
 
-        UITableColumn *column = [[[UITableColumn alloc]
-            initWithTitle:CYLocalize("NAME")
-            identifier:@"name"
-            width:[self frame].size.width
-        ] autorelease];
+        index_ = [[NSMutableArray alloc] initWithCapacity:32];
+        indices_ = [[NSMutableDictionary alloc] initWithCapacity:32];
 
-        UITable *table = [list_ table];
-        [table setSeparatorStyle:1];
-        [table addTableColumn:column];
-        [table setDelegate:self];
-        [table setReusesTableCells:YES];
+        packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
+        sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
 
+        list_ = [[UITableView alloc] initWithFrame:[self bounds] style:UITableViewStylePlain];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [list_ setRowHeight:73.0f];
         [self addSubview:list_];
 
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
+        [list_ setDataSource:self];
+        [list_ setDelegate:self];
     } return self;
 }
 
@@ -5024,6 +5498,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 [packages_ addObject:package];
     _end
 
+    [index_ removeAllObjects];
+    [indices_ removeAllObjects];
+
     Section *section = nil;
 
     _profile(PackageTable$reloadData$Section)
@@ -5041,6 +5518,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                     section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
                 _end
 
+                [index_ addObject:[section name]];
+                //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index];
+
                 _profile(PackageTable$reloadData$Section$Add)
                     [sections_ addObject:section];
                 _end
@@ -5055,24 +5535,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     _end
 }
 
-- (NSString *) title {
-    return title_;
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
-}
-
 - (void) resetCursor {
-    [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
+    [list_ scrollRectToVisible:CGRectMake(0, 0, 0, 0) animated:NO];
 }
 
-- (UISectionList *) list {
+- (UITableView *) list {
     return list_;
 }
 
 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
-    [list_ setShouldHideHeaderInShortLists:hide];
+    //XXX:[list_ setShouldHideHeaderInShortLists:hide];
 }
 
 @end
@@ -5085,8 +5557,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
 
@@ -5098,6 +5571,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];
@@ -5107,23 +5590,21 @@ 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];
-
-        /* 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);
-
+- (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;
 }
@@ -5131,19 +5612,86 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Add Source View {{{ */
-@interface AddSourceView : RVPage {
+/* 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];
+}
+
+- (id) 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 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;
 }
@@ -5151,44 +5699,78 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Source Cell {{{ */
-@interface SourceCell : UITableCell {
+@interface SourceCell : UITableViewCell {
     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)];
 
@@ -5197,27 +5779,24 @@ 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_ ellipsis:2];
 
     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_ ellipsis:2];
 
     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_ ellipsis:2];
 }
 
 @end
 /* }}} */
 /* Source Table {{{ */
-@interface SourceTable : RVPage {
+@interface SourceTable : CYViewController {
     _transient Database *database_;
-    UISectionList *list_;
+    UITableView *list_;
     NSMutableArray *sources_;
-    UIActionSheet *alert_;
     int offset_;
 
     NSString *href_;
@@ -5225,14 +5804,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     NSError *error_;
 
     //NSURLConnection *installer_;
+    NSURLConnection *trivial_;
     NSURLConnection *trivial_bz2_;
     NSURLConnection *trivial_gz_;
     //NSURLConnection *automatic_;
 
-    BOOL trivial_;
+    BOOL cydia_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 
 @end
 
@@ -5247,9 +5827,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) dealloc {
-    [[list_ table] setDelegate:nil];
-    [list_ setDataSource:nil];
-
     if (href_ != nil)
         [href_ release];
     if (hud_ != nil)
@@ -5258,6 +5835,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [error_ release];
 
     //[self _deallocConnection:installer_];
+    [self _deallocConnection:trivial_];
     [self _deallocConnection:trivial_gz_];
     [self _deallocConnection:trivial_bz2_];
     //[self _deallocConnection:automatic_];
@@ -5267,66 +5845,69 @@ 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];
+}
+
+- (int) numberOfSectionsInTableView:(UITableView *)tableView {
     return offset_ == 0 ? 1 : 2;
 }
 
-- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
+- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(int)section {
     switch (section + (offset_ == 0 ? 1 : 0)) {
-        case 0: return CYLocalize("ENTERED_BY_USER");
-        case 1: return CYLocalize("INSTALLED_BY_PACKAGE");
+        case 0: return UCLocalize("ENTERED_BY_USER");
+        case 1: return UCLocalize("INSTALLED_BY_PACKAGE");
 
-        default:
-            _assert(false);
-            return nil;
+        _nodefault
     }
 }
 
-- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
-    switch (section + (offset_ == 0 ? 1 : 0)) {
-        case 0: return 0;
-        case 1: return offset_;
+- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)section {
+    int count = [sources_ count];
+    switch (section) {
+        case 0: return (offset_ == 0 ? count : offset_);
+        case 1: return count - offset_;
 
-        default:
-            _assert(false);
-            return -1;
+        _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]];
+    
+    return cell;
 }
 
-- (BOOL) table:(UITable *)table canSelectRow:(int)row {
-    return YES;
+- (int) tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath {
+    return 1; //UITableViewCellAccessoryDisclosureIndicator?
 }
 
-- (void) tableRowSelected:(NSNotification*)notification {
-    UITable *table([list_ table]);
-    int row([table selectedRow]);
-    if (row == INT_MAX)
-        return;
-
-    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
@@ -5334,20 +5915,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];
 }
@@ -5368,7 +5945,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (colon.location != NSNotFound)
         href = [href substringFromIndex:(colon.location + 3)];
     href = [href stringByAddingPercentEscapes];
-    href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href];
+    href = [CydiaURL(@"api/repotag/") stringByAppendingString:href];
     href = [href stringByCachingURLWithCurrentCDN];
 
     NSURL *url([NSURL URLWithString:href]);
@@ -5383,7 +5960,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) _endConnection:(NSURLConnection *)connection {
     NSURLConnection **field = NULL;
-    if (connection == trivial_bz2_)
+    if (connection == trivial_)
+        field = &trivial_;
+    else if (connection == trivial_bz2_)
         field = &trivial_bz2_;
     else if (connection == trivial_gz_)
         field = &trivial_gz_;
@@ -5392,51 +5971,51 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     *field = nil;
 
     if (
+        trivial_ == nil &&
         trivial_bz2_ == nil &&
         trivial_gz_ == nil
     ) {
         bool defer(false);
 
-        if (trivial_) {
+        if (cydia_) {
             if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) {
                 defer = true;
 
-                UIActionSheet *sheet = [[[UIActionSheet alloc]
-                    initWithTitle:CYLocalize("SOURCE_WARNING")
-                    buttons:[NSArray arrayWithObjects:CYLocalize("ADD_ANYWAY"), CYLocalize("CANCEL"), nil]
-                    defaultButtonIndex:0
+                UIAlertView *alert = [[[UIAlertView alloc]
+                    initWithTitle:UCLocalize("SOURCE_WARNING")
+                    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]
-                initWithTitle:CYLocalize("VERIFICATION_ERROR")
-                buttons:[NSArray arrayWithObjects:CYLocalize("OK"), nil]
-                defaultButtonIndex:0
+            UIAlertView *alert = [[[UIAlertView alloc]
+                initWithTitle:UCLocalize("VERIFICATION_ERROR")
+                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]
-                initWithTitle:CYLocalize("NOT_REPOSITORY")
-                buttons:[NSArray arrayWithObjects:CYLocalize("OK"), nil]
-                defaultButtonIndex:0
+            UIAlertView *alert = [[[UIAlertView alloc]
+                initWithTitle:UCLocalize("NOT_REPOSITORY")
+                message:UCLocalize("NOT_REPOSITORY_EX")
                 delegate:self
-                context:@"trivial"
+                cancelButtonTitle:UCLocalize("OK")
+                otherButtonTitles:nil
             ] autorelease];
 
-            [sheet setBodyText:CYLocalize("NOT_REPOSITORY_EX")];
-            [sheet popupAlertAnimated:YES];
+            [alert setContext:@"trivial"];
+            [alert show];
         }
 
         [delegate_ setStatusBarShowsProgress:NO];
@@ -5460,7 +6039,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
     switch ([response statusCode]) {
         case 200:
-            trivial_ = YES;
+            cydia_ = YES;
     }
 }
 
@@ -5475,11 +6054,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [self _endConnection:connection];
 }
 
+- (id)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];
@@ -5488,20 +6069,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];
 
@@ -5511,81 +6091,70 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                     href_ = href;
                 href_ = [href_ retain];
 
+                trivial_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages"] method:@"HEAD"] retain];
                 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
                 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
                 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
 
-                trivial_ = false;
+                cydia_ = false;
 
                 hud_ = [[delegate_ addProgressHUD] retain];
-                [hud_ setText:CYLocalize("VERIFYING_URL")];
+                [hud_ setText:UCLocalize("VERIFYING_URL")];
             } break;
 
-            case 2:
+            case 0:
             break;
 
-            default:
-                _assert(false);
+            _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;
 
-            default:
-                _assert(false);
+            _nodefault
         }
 
         [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:CYLocalize("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;
 }
 
 - (void) reloadData {
     pkgSourceList list;
-    _assert(list.ReadMainList());
+    if (!list.ReadMainList())
+        return;
 
     [sources_ removeAllObjects];
     [sources_ addObjectsFromArray:[database_ sources]];
@@ -5593,140 +6162,159 @@ 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]
-        initWithTitle:CYLocalize("ENTER_APT_URL")
-        buttons:[NSArray arrayWithObjects:CYLocalize("ADD_SOURCE"), CYLocalize("CANCEL"), nil]
-        defaultButtonIndex:0
+    UIAlertView *alert = [[[UIAlertView alloc]
+        initWithTitle:UCLocalize("ENTER_APT_URL")
+        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];
+    [alert show];
 }
 
-- (void) _rightButtonClicked {
-    UITable *table = [list_ table];
-    BOOL editing = [table isRowDeletionEnabled];
-    [table enableRowDeletion:!editing animated:YES];
-    [book_ reloadButtonsForPage:self];
-}
-
-- (NSString *) title {
-    return CYLocalize("SOURCES");
-}
-
-- (NSString *) leftButtonTitle {
-    return [[list_ table] isRowDeletionEnabled] ? CYLocalize("ADD") : nil;
+- (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];
+    }
 }
 
-- (id) rightButtonTitle {
-    return [[list_ table] isRowDeletionEnabled] ? CYLocalize("DONE") : CYLocalize("EDIT");
+- (void) settingsButtonClicked {
+    [delegate_ showSettings];
 }
 
-- (UINavigationButtonStyle) rightButtonStyle {
-    return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+- (void) editButtonClicked {
+    [list_ setEditing:![list_ isEditing] animated:YES];
+    
+    [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;
 
 @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_];
+- (id) 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 CYLocalize("INSTALLED");
 }
 
-- (NSString *) backButtonTitle {
-    return CYLocalize("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_ ? CYLocalize("EXPERT") : CYLocalize("SIMPLE");
-}
+- (void) roleButtonClicked {
+    [packages_ setObject:[NSNumber numberWithBool:expert_]];
+    [packages_ reloadData];
+    expert_ = !expert_;
 
-- (UINavigationButtonStyle) rightButtonStyle {
-    return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
+    [self updateRoleButton];
 }
 
 - (void) setDelegate:(id)delegate {
@@ -5737,90 +6325,124 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Home View {{{ */
-@interface HomeView : BrowserView {
+/* Home Controller {{{ */
+@interface HomeController : CYBrowserController {
 }
 
 @end
 
-@implementation HomeView
+@implementation HomeController
 
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    NSString *context([sheet context]);
-
-    if ([context isEqualToString:@"about"])
-        [sheet dismiss];
-    else
-        [super alertSheet:sheet buttonClicked:button];
+- (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:CYLocalize("ABOUT_CYDIA")
-        buttons:[NSArray arrayWithObjects:CYLocalize("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 CYLocalize("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 : BrowserView {
+/* Manage Controller {{{ */
+@interface ManageController : CYBrowserController {
 }
 
 @end
 
-@implementation ManageView
+@implementation ManageController
 
-- (NSString *) title {
-    return CYLocalize("MANAGE");
-}
-
-- (void) _leftButtonClicked {
-    [delegate_ askForSettings];
+- (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];
+    
+        [self queueStatusDidChange];
+    } return self;
 }
 
-- (NSString *) leftButtonTitle {
-    return CYLocalize("SETTINGS");
+- (void) settingsButtonClicked {
+    [delegate_ showSettings];
 }
 
 #if !AlwaysReload
-- (id) _rightButtonTitle {
-    return Queuing_ ? CYLocalize("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
 
+- (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;
 }
@@ -5828,248 +6450,189 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-#include <BrowserView.m>
-
-/* 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_;
-}
+@implementation RefreshBar
+
+- (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];
+    
+    CGSize prgsize = {75, 100};
+    CGRect prgrect = {{
+        [self frame].size.width - prgsize.width - 10,
+        ([self frame].size.height - prgsize.height) / 2
+    } , prgsize};
+    [progress_ setFrame:prgrect];
+    
+    CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]);
+    unsigned indoffset = ([self frame].size.height - indsize.height) / 2;
+    CGRect indrect = {{indoffset, indoffset}, indsize};
+    [indicator_ setFrame:indrect];
+    
+    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)setFrame:(CGRect)frame {
+    [super setFrame:frame];
+    
+    [self positionViews];
+}
+
+- (id) initWithFrame:(CGRect)frame delegate:(id)delegate {
+    if ((self = [super initWithFrame:frame])) {
+        [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
+
+        [self setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
+        [self setBarStyle:1];
+
+        int barstyle([self _barStyle:NO]);
+        bool ugly(barstyle == 0);
 
-- (void) update {
-    [UIView beginAnimations:nil context:NULL];
+        UIProgressIndicatorStyle style = ugly ?
+            UIProgressIndicatorStyleMediumBrown :
+            UIProgressIndicatorStyleMediumWhite;
 
-    CGRect ovrframe = [overlay_ frame];
-    ovrframe.origin.y = 0;
-    [overlay_ setFrame:ovrframe];
+        indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectZero];
+        [indicator_ setStyle:style];
+        [indicator_ startAnimation];
+        [self addSubview:indicator_];
 
-    CGRect barframe = [navbar_ frame];
-    barframe.origin.y += ovrframe.size.height;
-    [navbar_ setFrame:barframe];
+        prompt_ = [[UITextLabel alloc] initWithFrame:CGRectZero];
+        [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
+        [prompt_ setBackgroundColor:[UIColor clearColor]];
+        [prompt_ setFont:[UIFont systemFontOfSize:15]];
+        [self addSubview:prompt_];
 
-    CGRect trnframe = [transition_ frame];
-    trnframe.origin.y += ovrframe.size.height;
-    trnframe.size.height -= ovrframe.size.height;
-    [transition_ setFrame:trnframe];
+        progress_ = [[UIProgressBar alloc] initWithFrame:CGRectZero];
+        [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin];
+        [progress_ setStyle:0];
+        [self addSubview:progress_];
+        
+        cancel_ = [[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted];
+        [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+        [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside];
+        [cancel_ setBarStyle:barstyle];
+        
+        [self positionViews];
+    } return self;
+}
 
-    [UIView endAnimations];
+- (void) cancel {
+    [cancel_ removeFromSuperview];
+}
 
-    [indicator_ startAnimation];
-    [prompt_ setText:CYLocalize("UPDATING_DATABASE")];
+- (void) start {
+    [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
     [progress_ setProgress:0];
+    [self addSubview:cancel_];
+}
 
-    updating_ = true;
-    [overlay_ addSubview:cancel_];
-
-    [NSThread
-        detachNewThreadSelector:@selector(_update)
-        toTarget:self
-        withObject:nil
-    ];
+- (void) stop {
+    [cancel_ removeFromSuperview];
 }
 
-- (void) _update_ {
-    updating_ = false;
+- (void) setPrompt:(NSString *)prompt {
+    [prompt_ setText:prompt];
+}
 
-    [indicator_ stopAnimation];
+- (void) setProgress:(float)progress {
+    [progress_ setProgress:progress];
+}
 
-    [UIView beginAnimations:nil context:NULL];
+@end
+/* }}} */
 
-    CGRect ovrframe = [overlay_ frame];
-    ovrframe.origin.y = -ovrframe.size.height;
-    [overlay_ setFrame:ovrframe];
+/* Cydia Tab Bar Controller {{{ */
+@interface CYTabBarController : UITabBarController {
+    Database *database_;
+}
 
-    CGRect barframe = [navbar_ frame];
-    barframe.origin.y -= ovrframe.size.height;
-    [navbar_ setFrame:barframe];
+@end    
 
-    CGRect trnframe = [transition_ frame];
-    trnframe.origin.y -= ovrframe.size.height;
-    trnframe.size.height += ovrframe.size.height;
-    [transition_ setFrame:trnframe];
+@implementation CYTabBarController
 
-    [UIView commitAnimations];
+/* XXX: some logic should probably go here related to
+freeing the view controllers on tab change */
 
-    [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+- (void) reloadData {
+    size_t count([[self viewControllers] count]);
+    for (size_t i(0); i != count; ++i) {
+        UIViewController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
+        [page reloadData];
+    }
 }
 
-- (id) initWithFrame:(CGRect)frame database:(Database *)database {
-    if ((self = [super initWithFrame:frame]) != nil) {
+- (id) initWithDatabase: (Database *)database {
+    if ((self = [super init]) != nil) {
         database_ = database;
+    } return self;
+}
 
-        CGRect ovrrect = [navbar_ bounds];
-        ovrrect.size.height = [UINavigationBar defaultSize].height;
-        ovrrect.origin.y = -ovrrect.size.height;
+@end
+/* }}} */
 
-        overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
-        [self addSubview:overlay_];
+/* Cydia Navigation Controller {{{ */
+@interface CYNavigationController : UINavigationController <
+    ProgressDelegate
+> {
+    _transient Database *database_;
+    id delegate_;
+}
 
-        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_];
+- (id) initWithDatabase:(Database *)database;
+- (void) reloadData;
 
-        [overlay_ setBarStyle:1];
-        [underlay_ setBarStyle:1];
+@end
 
-        int barstyle = [overlay_ _barStyle:NO];
-        bool ugly = barstyle == 0;
 
-        UIProgressIndicatorStyle style = ugly ?
-            UIProgressIndicatorStyleMediumBrown :
-            UIProgressIndicatorStyleMediumWhite;
+@implementation CYNavigationController
 
-        CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
-        unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
-        CGRect indrect = {{indoffset, indoffset}, indsize};
+- (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];
+    }
+}
 
-        indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
-        [indicator_ setStyle:style];
-        [overlay_ addSubview:indicator_];
+- (void) dealloc {
+    [super dealloc];
+}
 
-        CGSize prmsize = {215, indsize.height + 4};
+- (void) reloadData {
+    size_t count([[self viewControllers] count]);
+    for (size_t i(0); i != count; ++i) {
+        UIViewController *page([[self viewControllers] objectAtIndex:(count - i - 1)]);
+        [page reloadData];
+    }
+}
 
-        CGRect prmrect = {{
-            indoffset * 2 + indsize.width,
-#ifdef __OBJC2__
-            -1 +
-#endif
-            unsigned(ovrrect.size.height - prmsize.height) / 2
-        }, prmsize};
+- (void) setDelegate:(id)delegate {
+    delegate_ = delegate;
+}
 
-        UIFont *font = [UIFont systemFontOfSize:15];
-
-        prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
-
-        [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
-        [prompt_ setBackgroundColor:[UIColor clearColor]];
-        [prompt_ setFont:font];
-
-        [overlay_ addSubview:prompt_];
-
-        CGSize prgsize = {75, 100};
-
-        CGRect prgrect = {{
-            ovrrect.size.width - prgsize.width - 10,
-            (ovrrect.size.height - prgsize.height) / 2
-        } , prgsize};
-
-        progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
-        [progress_ setStyle:0];
-        [overlay_ addSubview:progress_];
-
-        cancel_ = [[UINavigationButton alloc] initWithTitle:CYLocalize("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_ setBarStyle:barstyle];
-    } return self;
-}
-
-- (void) _onCancel {
-    updating_ = false;
-    [cancel_ removeFromSuperview];
-}
-
-- (void) _update { _pooled
-    Status status;
-    status.setDelegate(self);
-
-    [database_ updateWithStatus:status];
-
-    [self
-        performSelectorOnMainThread:@selector(_update_)
-        withObject:nil
-        waitUntilDone:NO
-    ];
-}
-
-- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
-    [prompt_ setText:[NSString stringWithFormat:CYLocalize("COLON_DELIMITED"), CYLocalize("ERROR"), error]];
-}
-
-- (void) setProgressTitle:(NSString *)title {
-    [self
-        performSelectorOnMainThread:@selector(_setProgressTitle:)
-        withObject:title
-        waitUntilDone:YES
-    ];
-}
-
-- (void) setProgressPercent:(float)percent {
-    [self
-        performSelectorOnMainThread:@selector(_setProgressPercent:)
-        withObject:[NSNumber numberWithFloat:percent]
-        waitUntilDone:YES
-    ];
-}
-
-- (void) startProgress {
-}
-
-- (void) addProgressOutput:(NSString *)output {
-    [self
-        performSelectorOnMainThread:@selector(_addProgressOutput:)
-        withObject:output
-        waitUntilDone:YES
-    ];
-}
-
-- (bool) isCancelling:(size_t)received {
-    return !updating_;
-}
-
-- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
-    [sheet dismiss];
-}
-
-- (void) _setProgressTitle:(NSString *)title {
-    [prompt_ setText:title];
-}
-
-- (void) _setProgressPercent:(NSNumber *)percent {
-    [progress_ setProgress:[percent floatValue]];
-}
-
-- (void) _addProgressOutput:(NSString *)output {
-}
+- (id) initWithDatabase:(Database *)database {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+    } return self;
+}
 
 @end
 /* }}} */
@@ -6174,24 +6737,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
-/* Sections View {{{ */
-@interface SectionsView : RVPage {
+/* Sections Controller {{{ */
+@interface SectionsController : CYViewController {
     _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;
 
 @end
 
-@implementation SectionsView
+@implementation SectionsController
 
 - (void) dealloc {
     [list_ setDataSource:nil];
@@ -6199,105 +6761,90 @@ 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;
+- (int) tableView:(UITableView *)tableView numberOfRowsInSection:(int)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_];
+
+    return cell;
+}
 
-    Section *section;
-    NSString *name;
+- (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 = CYLocalize("ALL_PACKAGES");
+        title = UCLocalize("ALL_PACKAGES");
     } else {
-        section = [filtered_ objectAtIndex:(row - 1)];
-        name = [section name];
-
-        if (name != nil)
+        if (name != nil) {
+            name = [NSString stringWithString:name];
             title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
-        else {
+        else {
             name = @"";
-            title = CYLocalize("NO_SECTION");
+            title = UCLocalize("NO_SECTION");
         }
     }
 
-    PackageTable *table = [[[FilteredPackageTable alloc]
-        initWithBook:book_
-        database:database_
+    FilteredPackageController *table = [[[FilteredPackageController alloc]
+        initWithDatabase:database_
         title:title
-        filter:@selector(isVisiblyUninstalledInSection:)
+        filter:@selector(isVisibleInSection:)
         with:name
     ] autorelease];
 
     [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) {
+- (id) 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:CYLocalize("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;
 }
 
@@ -6335,7 +6882,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [*section addToCount];
 
         _profile(SectionsView$reloadData$Filter)
-            if (![package valid] || [package installed] != nil || ![package visible])
+            if (![package valid] || ![package visible])
                 continue;
         _end
 
@@ -6356,7 +6903,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [section addToCount];
 
         _profile(SectionsView$reloadData$Filter)
-            if (![package valid] || [package installed] != nil || ![package visible])
+            if (![package valid] || ![package visible])
                 continue;
         _end
 
@@ -6384,42 +6931,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_ ? CYLocalize("SECTION_VISIBILITY") : CYLocalize("INSTALL_BY_SECTION");
-}
-
-- (NSString *) backButtonTitle {
-    return CYLocalize("SECTIONS");
-}
-
-- (id) rightButtonTitle {
-    return [sections_ count] == 0 ? nil : editing_ ? CYLocalize("DONE") : CYLocalize("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 {
@@ -6428,24 +6966,25 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 /* }}} */
-/* Changes View {{{ */
-@interface ChangesView : RVPage {
+/* Changes Controller {{{ */
+@interface ChangesController : CYViewController {
     _transient Database *database_;
     NSMutableArray *packages_;
     NSMutableArray *sections_;
-    UISectionList *list_;
+    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_ table] setDelegate:nil];
+    [list_ setDelegate:nil];
     [list_ setDataSource:nil];
 
     [packages_ release];
@@ -6454,100 +6993,96 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
-- (int) numberOfSectionsInSectionList:(UISectionList *)list {
-    return [sections_ count];
+- (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];
+    }
 }
 
-- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
-    return [[sections_ objectAtIndex:section] name];
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
+    NSInteger count([sections_ count]);
+    return count == 0 ? 1 : count;
 }
 
-- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
-    return [[sections_ objectAtIndex:section] row];
+- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
+    if ([sections_ count] == 0)
+        return nil;
+    return [[sections_ objectAtIndex:section] name];
 }
 
-- (int) numberOfRowsInTable:(UITable *)table {
-    return [packages_ count];
+- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
+    if ([sections_ count] == 0)
+        return 0;
+    return [[sections_ objectAtIndex:section] count];
 }
 
-- (float) table:(UITable *)table heightForRow:(int)row {
-    return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
+- (Package *) packageAtIndexPath:(NSIndexPath *)path {
+    Section *section([sections_ objectAtIndex:[path section]]);
+    NSInteger row([path row]);
+    return [packages_ objectAtIndex:([section row] + row)];
 }
 
-- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
-    if (reusing == nil)
-        reusing = [[[PackageCell alloc] init] autorelease];
-    [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
-    return reusing;
+- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
+    PackageCell *cell([table dequeueReusableCellWithIdentifier:@"Package"]);
+    if (cell == nil)
+        cell = [[[PackageCell alloc] init] autorelease];
+    [cell setPackage:[self packageAtIndexPath:path]];
+    return cell;
 }
 
-- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
-    return NO;
-}
+/*- (CGFloat) tableView:(UITableView *)table heightForRowAtIndexPath:(NSIndexPath *)path {
+    return [PackageCell heightForPackage:[self packageAtIndexPath:path]];
+}*/
 
-- (void) tableRowSelected:(NSNotification *)notification {
-    int row = [[notification object] selectedRow];
-    if (row == INT_MAX)
-        return;
-    Package *package = [packages_ objectAtIndex:row];
-    PackageView *view([delegate_ packageView]);
+- (NSIndexPath *) tableView:(UITableView *)table willSelectRowAtIndexPath:(NSIndexPath *)path {
+    Package *package([self packageAtIndexPath:path]);
+    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 {
+    [[UIApplication sharedApplication] beginUpdate];
+    [[self navigationItem] setLeftBarButtonItem:nil];
 }
 
-- (void) _rightButtonClicked {
+- (void) upgradeButtonClicked {
     [delegate_ distUpgrade];
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
+- (id) 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_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
-        [self addSubview:list_];
+        list_ = [[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain];
+        [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+        [list_ setRowHeight:73.0f];
+        [[self view] addSubview:list_];
 
-        [list_ setShouldHideHeaderInShortLists:NO];
         [list_ setDataSource:self];
-        //[list_ setSectionListStyle:1];
-
-        UITableColumn *column = [[[UITableColumn alloc]
-            initWithTitle:CYLocalize("NAME")
-            identifier:@"name"
-            width:[self frame].size.width
-        ] autorelease];
-
-        UITable *table = [list_ table];
-        [table setSeparatorStyle:1];
-        [table addTableColumn:column];
-        [table setDelegate:self];
-        [table setReusesTableCells:YES];
-
-        [self reloadData];
+        [list_ setDelegate:self];
 
-        [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 (
-            [package installed] == nil && [package valid] && [package visible] ||
+            [package uninstalled] && [package valid] && [package visible] ||
             [package upgradableAndEssential:YES]
         )
             [packages_ addObject:package];
@@ -6555,9 +7090,23 @@ 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:CYLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
-    Section *ignored = [[[Section alloc] initWithName:CYLocalize("IGNORED_UPGRADES") localize:NO] autorelease];
+    Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
+    Section *ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") localize:NO] autorelease];
     Section *section = nil;
     NSDate *last = nil;
 
@@ -6575,7 +7124,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
             unseens = true;
             NSDate *seen;
 
-            _profile(ChangesView$reloadData$Remember)
+            _profile(ChangesController$reloadData$Remember)
                 seen = [package seen];
             _end
 
@@ -6584,14 +7133,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
                 NSString *name;
                 if (seen == nil)
-                    name = CYLocalize("UNKNOWN");
+                    name = UCLocalize("UNKNOWN");
                 else {
                     name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
                     [name autorelease];
                 }
 
-                _profile(ChangesView$reloadData$Allocate)
-                    name = [NSString stringWithFormat:CYLocalize("NEW_AT"), name];
+                _profile(ChangesController$reloadData$Allocate)
+                    name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name];
                     section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease];
                     [sections_ addObject:section];
                 _end
@@ -6622,289 +7171,96 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         [sections_ insertObject:upgradable atIndex:0];
 
     [list_ reloadData];
-    [self reloadButtons];
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-    [list_ resetViewAnimated:animated];
-}
-
-- (NSString *) leftButtonTitle {
-    return [(CYBook *)book_ updating] ? nil : CYLocalize("REFRESH");
-}
-
-- (id) rightButtonTitle {
-    return upgrades_ == 0 ? nil : [NSString stringWithFormat:CYLocalize("PARENTHETICAL"), CYLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", upgrades_]];
-}
 
-- (NSString *) title {
-    return CYLocalize("CHANGES");
+    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 (![[UIApplication sharedApplication] updating]) [[self navigationItem] setLeftBarButtonItem:leftItem];
+    [leftItem release];
 }
 
 @end
 /* }}} */
-/* Search View {{{ */
-@protocol SearchViewDelegate
-- (void) showKeyboard:(BOOL)show;
-@end
-
-@interface SearchView : RVPage {
-    UIView *accessory_;
-    UISearchField *field_;
-    UITransitionView *transition_;
-    FilteredPackageTable *table_;
-    UIPreferencesTable *advanced_;
-    UIView *dimmed_;
-    bool flipped_;
-    bool reload_;
+/* Search Controller {{{ */
+@interface SearchController : FilteredPackageController {
+    id search_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database;
+- (id) initWithDatabase:(Database *)database;
 - (void) reloadData;
 
 @end
 
-@implementation SearchView
+@implementation SearchController
 
 - (void) dealloc {
-    [field_ setDelegate:nil];
-
-    [accessory_ release];
-    [field_ release];
-    [transition_ release];
-    [table_ release];
-    [advanced_ release];
-    [dimmed_ release];
+    [search_ release];
     [super dealloc];
 }
 
-- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
-    return 1;
-}
-
-- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
-    switch (group) {
-        case 0: return [NSString stringWithFormat:CYLocalize("PARENTHETICAL"), CYLocalize("ADVANCED_SEARCH"), CYLocalize("COMING_SOON")];
-
-        default: _assert(false);
-    }
-}
-
-- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
-    switch (group) {
-        case 0: return 0;
-
-        default: _assert(false);
-    }
-}
-
-- (void) _showKeyboard:(BOOL)show {
-    CGSize keysize = [UIKeyboard defaultSize];
-    CGRect keydown = [book_ pageBounds];
-    CGRect keyup = keydown;
-    keyup.size.height -= keysize.height - ButtonBarHeight_;
-
-    float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
-
-    UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
-    [animation setSignificantRectFields:8];
-
-    if (show) {
-        [animation setStartFrame:keydown];
-        [animation setEndFrame:keyup];
-    } else {
-        [animation setStartFrame:keyup];
-        [animation setEndFrame:keydown];
-    }
-
-    UIAnimator *animator = [UIAnimator sharedAnimator];
-
-    [animator
-        addAnimations:[NSArray arrayWithObjects:animation, nil]
-        withDuration:(KeyboardTime_ - delay)
-        start:!show
-    ];
-
-    if (show)
-        [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
-
-    [delegate_ showKeyboard:show];
-}
-
-- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
-    [self _showKeyboard:YES];
-}
-
-- (void) textFieldDidResignFirstResponder:(UITextField *)field {
-    [self _showKeyboard:NO];
+- (void) searchBarSearchButtonClicked:(id)searchBar {
+    [packages_ setObject:[search_ text] forFilter:@selector(isUnfilteredAndSearchedForBy:)];
+    [search_ resignFirstResponder];
+    [self reloadData];
 }
 
-- (void) keyboardInputChanged:(UIFieldEditor *)editor {
-    if (reload_) {
-        NSString *text([field_ text]);
-        [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
-        [self reloadData];
-        reload_ = false;
-    }
+- (void) searchBar:(id)searchBar textDidChange:(NSString *)text {
+    [packages_ setObject:text forFilter:@selector(isUnfilteredAndSelectedForBy:)];
+    [self reloadData];
 }
 
-- (void) textFieldClearButtonPressed:(UITextField *)field {
-    reload_ = true;
-}
+- (id) title { return nil; }
 
-- (void) keyboardInputShouldDelete:(id)input {
-    reload_ = true;
+- (id) initWithDatabase:(Database *)database {
+    return [super initWithDatabase:database title:UCLocalize("SEARCH") filter:@selector(isUnfilteredAndSearchedForBy:) with:nil];
 }
 
-- (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)viewDidAppear:(BOOL)animated {
+       [super viewDidAppear:animated];
+       if (!search_) {
+        search_ = [[objc_getClass("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];
     }
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database {
-    if ((self = [super initWithBook:book]) != nil) {
-        CGRect pageBounds = [book_ pageBounds];
-
-        transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
-        [self addSubview:transition_];
-
-        advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
-
-        [advanced_ setReusesTableCells:YES];
-        [advanced_ setDataSource:self];
-        [advanced_ reloadData];
-
-        dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
-        CGColor dimmed(space_, 0, 0, 0, 0.5);
-        [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
-
-        table_ = [[FilteredPackageTable alloc]
-            initWithBook:book
-            database:database
-            title:nil
-            filter:@selector(isUnfilteredAndSearchedForBy:)
-            with:nil
-        ];
-
-        [table_ setShouldHideHeaderInShortLists:NO];
-        [transition_ transition:0 toView:table_];
-
-        CGRect cnfrect = {{
-#ifdef __OBJC2__
-        6 +
-#endif
-        1, 38}, {17, 18}};
-
-        CGRect area;
-        area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
-        area.origin.y = 1;
-
-        area.size.width =
-#ifdef __OBJC2__
-            8 +
-#endif
-            [self bounds].size.width - area.origin.x - 18;
-
-        area.size.height = [UISearchField defaultHeight];
-
-        field_ = [[UISearchField alloc] initWithFrame:area];
-
-        UIFont *font = [UIFont systemFontOfSize:16];
-        [field_ setFont:font];
-
-        [field_ setPlaceholder:CYLocalize("SEARCH_EX")];
-        [field_ setDelegate:self];
-
-        [field_ setPaddingTop:5];
-
-        UITextInputTraits *traits([field_ textInputTraits]);
-        [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
-        [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
-        [traits setReturnKeyType:UIReturnKeySearch];
-
-        CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
-
-        accessory_ = [[UIView alloc] initWithFrame:accrect];
-        [accessory_ addSubview:field_];
-
-        /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
-        [configure setShowPressFeedback:YES];
-        [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
-        [configure addTarget:self action:@selector(configurePushed) forEvents:1];
-        [accessory_ addSubview:configure];*/
-
-        [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-        [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
-    } return self;
-}
-
-- (void) flipPage {
-#ifndef __OBJC2__
-    LKAnimation *animation = [LKTransition animation];
-    [animation setType:@"oglFlip"];
-    [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
-    [animation setFillMode:@"extended"];
-    [animation setTransitionFlags:3];
-    [animation setDuration:10];
-    [animation setSpeed:0.35];
-    [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
-    [[transition_ _layer] addAnimation:animation forKey:0];
-    [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
-    flipped_ = !flipped_;
-#endif
-}
-
-- (void) configurePushed {
-    [field_ resignFirstResponder];
-    [self flipPage];
-}
-
-- (void) resetViewAnimated:(BOOL)animated {
-    if (flipped_)
-        [self flipPage];
-    [table_ resetViewAnimated:animated];
-}
-
 - (void) _reloadData {
 }
 
 - (void) reloadData {
-    if (flipped_)
-        [self flipPage];
-    [table_ setObject:[field_ text]];
-    _profile(SearchView$reloadData)
-        [table_ reloadData];
+    _profile(SearchController$reloadData)
+        [packages_ reloadData];
     _end
     PrintTimes();
-    [table_ resetCursor];
-}
-
-- (UIView *) accessoryView {
-    return accessory_;
-}
-
-- (NSString *) title {
-    return nil;
-}
-
-- (NSString *) backButtonTitle {
-    return CYLocalize("SEARCH");
+    [packages_ resetCursor];
 }
 
-- (void) setDelegate:(id)delegate {
-    [table_ setDelegate:delegate];
-    [super setDelegate:delegate];
+- (void) didSelectPackage:(Package *)package {
+    [search_ resignFirstResponder];
+    [super didSelectPackage:package];
 }
 
 @end
 /* }}} */
-
-@interface SettingsView : RVPage {
+/* Settings Controller {{{ */
+@interface SettingsController : CYViewController {
     _transient Database *database_;
     NSString *name_;
     Package *package_;
@@ -6915,11 +7271,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     UIPreferencesControlTableCell *ignoredCell_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
+- (id) initWithDatabase:(Database *)database package:(NSString *)package;
 
 @end
 
-@implementation SettingsView
+@implementation SettingsController
 
 - (void) dealloc {
     [table_ setDataSource:nil];
@@ -6950,7 +7306,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         case 0: return nil;
         case 1: return nil;
 
-        default: _assert(false);
+        _nodefault
     }
 
     return nil;
@@ -6964,7 +7320,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         case 0: return NO;
         case 1: return YES;
 
-        default: _assert(false);
+        _nodefault
     }
 
     return NO;
@@ -6978,7 +7334,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         case 0: return 1;
         case 1: return 1;
 
-        default: _assert(false);
+        _nodefault
     }
 
     return 0;
@@ -7023,48 +7379,52 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 return subscribedCell_;
             case 1:
                 return ignoredCell_;
-            default: _assert(false);
+            _nodefault
         } break;
 
         case 1: switch (row) {
             case 0: {
                 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
                 [cell setShowSelection:NO];
-                [cell setTitle:CYLocalize("SHOW_ALL_CHANGES_EX")];
+                [cell setTitle:UCLocalize("SHOW_ALL_CHANGES_EX")];
                 return cell;
             }
 
-            default: _assert(false);
+            _nodefault
         } break;
 
-        default: _assert(false);
+        _nodefault
     }
 
     return nil;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
-    if ((self = [super initWithBook:book])) {
+- (id) title { return UCLocalize("SETTINGS"); }
+
+- (id) initWithDatabase:(Database *)database package:(NSString *)package {
+    if ((self = [super init])) {
         database_ = database;
         name_ = [package retain];
 
-        table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
-        [self addSubview:table_];
+        [[self navigationItem] setTitle:UCLocalize("SETTINGS")];
+
+        table_ = [[UIPreferencesTable alloc] initWithFrame:[[self view] bounds]];
+        [[self view] addSubview:table_];
 
         subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
-        [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
+        [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventTouchUpInside];
 
         ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
-        [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
+        [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventTouchUpInside];
 
         subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
         [subscribedCell_ setShowSelection:NO];
-        [subscribedCell_ setTitle:CYLocalize("SHOW_ALL_CHANGES")];
+        [subscribedCell_ setTitle:UCLocalize("SHOW_ALL_CHANGES")];
         [subscribedCell_ setControl:subscribedSwitch_];
 
         ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
         [ignoredCell_ setShowSelection:NO];
-        [ignoredCell_ setTitle:CYLocalize("IGNORE_UPGRADES")];
+        [ignoredCell_ setTitle:UCLocalize("IGNORE_UPGRADES")];
         [ignoredCell_ setControl:ignoredSwitch_];
 
         [table_ setDataSource:self];
@@ -7072,10 +7432,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     } return self;
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-    [table_ resetViewAnimated:animated];
-}
-
 - (void) reloadData {
     if (package_ != nil)
         [package_ autorelease];
@@ -7089,23 +7445,20 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [table_ reloadData];
 }
 
-- (NSString *) title {
-    return CYLocalize("SETTINGS");
-}
-
 @end
+/* }}} */
 
-/* Signature View {{{ */
-@interface SignatureView : BrowserView {
+/* Signature Controller {{{ */
+@interface SignatureController : CYBrowserController {
     _transient Database *database_;
     NSString *package_;
 }
 
-- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
+- (id) initWithDatabase:(Database *)database package:(NSString *)package;
 
 @end
 
-@implementation SignatureView
+@implementation SignatureController
 
 - (void) dealloc {
     [package_ release];
@@ -7117,106 +7470,575 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [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 package:(NSString *)package {
+    if ((self = [super init]) != nil) {
         database_ = database;
         package_ = [package retain];
         [self reloadData];
     } return self;
 }
 
-- (void) resetViewAnimated:(BOOL)animated {
-}
-
 - (void) reloadData {
     [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
 }
 
 @end
 /* }}} */
+/* Role Controller {{{ */
+@interface RoleController : CYViewController {
+    _transient Database *database_;
+    id roledelegate_;
+    UITableView *table_;
+    UISegmentedControl *segment_;
+    UIView *container_;
+}
+@end
 
-@interface Cydia : UIApplication <
-    ConfirmationViewDelegate,
-    ProgressViewDelegate,
-    SearchViewDelegate,
+@implementation RoleController
+- (void) dealloc {
+    [table_ release];
+    [segment_ release];
+    [container_ release];
+    
+    [super dealloc];
+}
+
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
+    if ((self = [super init])) {
+        database_ = database;
+        roledelegate_ = delegate;
+        
+        [[self navigationItem] setTitle:UCLocalize("WHO_ARE_YOU")];
+        
+        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];
+        }
+        
+        [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) resizeSegmentedControl {
+    CGFloat width = [[self view] frame].size.width;
+    [segment_ setFrame:CGRectMake(width / 32.0f, 0, width - (width / 32.0f * 2.0f), 44.0f)];
+}
+
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    
+    [self resizeSegmentedControl];
+}
+
+- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
+    [self resizeSegmentedControl];
+}
+
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+    [self resizeSegmentedControl];
+}
+
+- (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_]) {
+        Role_ = role;
+        
+        Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+            Role_, @"Role",
+        nil];
+
+        [Metadata_ setObject:Settings_ forKey:@"Settings"];
+
+        Changed_ = true;
+    
+        [roledelegate_ updateData];
+    }
+}
+
+- (void) segmentChanged:(UISegmentedControl *)control {
+    [self showDoneButton];
+}
+
+- (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
+/* }}} */
+
+/* Cydia Container {{{ */
+@interface CYContainer : UIViewController <ProgressDelegate> {
+    _transient Database *database_;
+    RefreshBar *refreshbar_;
+
+    bool dropped_;
+    bool updating_;
+    id updatedelegate_;
+    UIViewController *root_;
+}
+
+@end
+
+@implementation CYContainer
+
+// NOTE: UIWindow only sends the top controller these messages,
+//       So we have to forward them on.
+
+- (void) viewDidAppear:(BOOL)animated {
+    [super viewDidAppear:animated];
+    [root_ viewDidAppear:animated];
+}
+
+- (void) viewWillAppear:(BOOL)animated {
+    [super viewWillAppear:animated];
+    [root_ viewWillAppear:animated];
+}
+
+- (void) viewDidDisappear:(BOOL)animated {
+    [super viewDidDisappear:animated];
+    [root_ viewDidDisappear:animated];
+}
+
+- (void) viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    [root_ viewWillDisappear:animated];
+}
+
+- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
+    return YES; /* XXX: return YES; */
+}
+
+- (void) setRootController:(UIViewController *)controller {
+    root_ = controller;
+    [[self view] addSubview:[root_ view]];
+}
+
+- (void) setUpdate:(NSDate *)date {
+    [self beginUpdate];
+}
+
+- (void) beginUpdate {
+    [self dropBar:YES];
+    [refreshbar_ start];
+    
+    updating_ = true;
+    
+    [NSThread
+        detachNewThreadSelector:@selector(performUpdate)
+        toTarget:self
+        withObject:nil
+    ];
+}
+
+- (void) performUpdate { _pooled
+    Status status;
+    status.setDelegate(self);
+    [database_ updateWithStatus:status];
+
+    [self
+        performSelectorOnMainThread:@selector(completeUpdate)
+        withObject:nil
+        waitUntilDone:NO
+    ];
+}
+
+- (void) completeUpdate {
+    updating_ = false;
+    
+    [self raiseBar:YES];
+    [refreshbar_ stop];
+    [updatedelegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
+}
+
+- (void) cancelUpdate {
+    [refreshbar_ cancel];
+    [self completeUpdate];
+}
+
+- (void) cancelPressed {
+    [self cancelUpdate];
+}
+
+- (BOOL) updating {
+    return updating_;
+}
+
+- (void) setProgressError:(NSString *)error withTitle:(NSString *)title {
+    [refreshbar_ setPrompt:[NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), UCLocalize("ERROR"), error]];
+}
+
+- (void) startProgress {
+}
+
+- (void) setProgressTitle:(NSString *)title {
+    [self
+        performSelectorOnMainThread:@selector(_setProgressTitle:)
+        withObject:title
+        waitUntilDone:YES
+    ];
+}
+
+- (bool) isCancelling:(size_t)received {
+    return !updating_;
+}
+
+- (void) setProgressPercent:(float)percent {
+    [self
+        performSelectorOnMainThread:@selector(_setProgressPercent:)
+        withObject:[NSNumber numberWithFloat:percent]
+        waitUntilDone:YES
+    ];
+}
+
+- (void) addProgressOutput:(NSString *)output {
+    [self
+        performSelectorOnMainThread:@selector(_addProgressOutput:)
+        withObject:output
+        waitUntilDone:YES
+    ];
+}
+
+- (void) _setProgressTitle:(NSString *)title {
+    [refreshbar_ setPrompt:title];
+}
+
+- (void) _setProgressPercent:(NSNumber *)percent {
+    [refreshbar_ setProgress:[percent floatValue]];
+}
+
+- (void) _addProgressOutput:(NSString *)output {
+}
+
+- (void) setUpdateDelegate:(id)delegate {
+    updatedelegate_ = delegate;
+}
+
+- (void) dropBar:(BOOL)animated {
+    if (dropped_) return;
+    dropped_ = true;
+
+    [[self view] addSubview:refreshbar_];
+
+    if (animated) [UIView beginAnimations:nil context:NULL];
+    CGRect barframe = [refreshbar_ frame];
+    CGRect viewframe = [[root_ view] frame];
+    viewframe.origin.y += barframe.size.height + 20.0f;
+    viewframe.size.height -= barframe.size.height + 20.0f;
+    [[root_ view] setFrame:viewframe];
+    if (animated) [UIView commitAnimations];
+
+    // Ensure bar has the proper width for our view, it might have changed
+    barframe.size.width = viewframe.size.width;
+    [refreshbar_ setFrame:barframe];
+
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+- (void) raiseBar:(BOOL)animated {
+    if (!dropped_) return;
+    dropped_ = false;
+
+    [refreshbar_ removeFromSuperview];
+
+    if (animated) [UIView beginAnimations:nil context:NULL];
+    CGRect barframe = [refreshbar_ frame];
+    CGRect viewframe = [[root_ view] frame];
+    viewframe.origin.y -= barframe.size.height + 20.0f;
+    viewframe.size.height += barframe.size.height + 20.0f;
+    [[root_ view] setFrame:viewframe];
+    if (animated) [UIView commitAnimations];
+
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+    if (dropped_) {
+        [self raiseBar:NO];
+        [self dropBar:NO];
+    }
+    
+    // XXX: fix Apple's layout bug
+    [[root_ selectedViewController] _updateLayoutForStatusBarAndInterfaceOrientation];
+}
+
+- (void) dealloc {
+    [refreshbar_ release];
+    [super dealloc];
+}
+
+- (id) initWithDatabase: (Database *)database {
+    if ((self = [super init]) != nil) {
+        database_ = database;
+
+        [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+
+        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 <
+    ConfirmationControllerDelegate,
+    ProgressControllerDelegate,
     CydiaDelegate
 > {
     UIWindow *window_;
+    CYContainer *container_;
 
-    UIView *underlay_;
-    UIView *overlay_;
-    CYBook *book_;
-    UIToolbar *buttonbar_;
-
-    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_;
 
-    PackageView *package_;
+#if RecyclePackageViews
+    NSMutableArray *details_;
+#endif
+
+    bool loaded_;
 }
 
+- (UIViewController *) _pageForURL:(NSURL *)url withClass:(Class)_class;
+- (void) setPage:(UIViewController *)page;
+
 @end
 
+static _finline void _setHomePage(Cydia *self) {
+    [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]
-            initWithTitle:(count == 1 ? CYLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:CYLocalize("HALFINSTALLED_PACKAGES"), count])
-            buttons:[NSArray arrayWithObjects:
-                CYLocalize("FORCIBLY_CLEAR"),
-                CYLocalize("TEMPORARY_IGNORE"),
-            nil]
-            defaultButtonIndex:0
+        UIAlertView *alert = [[[UIAlertView alloc]
+            initWithTitle:(count == 1 ? UCLocalize("HALFINSTALLED_PACKAGE") : [NSString stringWithFormat:UCLocalize("HALFINSTALLED_PACKAGES"), count])
+            message:UCLocalize("HALFINSTALLED_PACKAGE_EX")
             delegate:self
-            context:@"fixhalf"
+            cancelButtonTitle:UCLocalize("FORCIBLY_CLEAR")
+            otherButtonTitles:UCLocalize("TEMPORARY_IGNORE"), nil
         ] autorelease];
 
-        [sheet setBodyText:CYLocalize("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]
-            initWithTitle:(count == 1 ? CYLocalize("ESSENTIAL_UPGRADE") : [NSString stringWithFormat:CYLocalize("ESSENTIAL_UPGRADES"), count])
-            buttons:[NSArray arrayWithObjects:
-                CYLocalize("UPGRADE_ESSENTIAL"),
-                CYLocalize("COMPLETE_UPGRADE"),
-                CYLocalize("TEMPORARY_IGNORE"),
-            nil]
-            defaultButtonIndex:0
+        UIAlertView *alert = [[[UIAlertView alloc]
+            initWithTitle:(count == 1 ? UCLocalize("ESSENTIAL_UPGRADE") : [NSString stringWithFormat:UCLocalize("ESSENTIAL_UPGRADES"), count])
+            message:UCLocalize("ESSENTIAL_UPGRADE_EX")
             delegate:self
-            context:@"upgrade"
+            cancelButtonTitle:UCLocalize("TEMPORARY_IGNORE")
+            otherButtonTitles:UCLocalize("UPGRADE_ESSENTIAL"), UCLocalize("COMPLETE_UPGRADE"), nil
         ] autorelease];
 
-        [sheet setBodyText:CYLocalize("ESSENTIAL_UPGRADE_EX")];
-        [sheet popupAlertAnimated:YES];
+        [alert setContext:@"upgrade"];
+        [alert show];
+    }
+}
+
+- (void) _saveConfig {
+    if (Changed_) {
+        _trace();
+        NSString *error(nil);
+        if (NSData *data = [NSPropertyListSerialization dataFromPropertyList:Metadata_ format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]) {
+            _trace();
+            NSError *error(nil);
+            if (![data writeToFile:@"/var/lib/cydia/metadata.plist" options:NSAtomicWrite error:&error])
+                NSLog(@"failure to save metadata data: %@", error);
+            _trace();
+        } else {
+            NSLog(@"failure to serialize metadata: %@", error);
+            return;
+        }
+
+        Changed_ = false;
+    }
+}
+
+- (void) _updateData {
+    [self _saveConfig];
+
+    /* XXX: this is just stupid */
+    if (tag_ != 1 && sections_ != nil)
+        [sections_ reloadData];
+    if (tag_ != 2 && changes_ != nil)
+        [changes_ reloadData];
+    if (tag_ != 4 && search_ != nil)
+        [search_ reloadData];
+
+    [[tabbar_ selectedViewController] reloadData];
+}
+
+- (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];
+    
+    Reachability* reachability = [Reachability reachabilityWithHostName:@"cydia.saurik.com"];
+    NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
+    
+    if (loaded_ || ManualRefresh || remoteHostStatus == NotReachable) 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 {
     UIView *block();
 
-    static bool loaded(false);
     UIProgressHUD *hud([self addProgressHUD]);
-    [hud setText:(loaded ? CYLocalize("RELOADING_DATA") : CYLocalize("LOADING_DATA"))];
-    loaded = true;
+    [hud setText:(loaded_ ? UCLocalize("RELOADING_DATA") : UCLocalize("LOADING_DATA"))];
 
     [database_ yieldToSelector:@selector(reloadData) withObject:nil];
     _trace();
@@ -7228,7 +8050,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [essential_ removeAllObjects];
     [broken_ removeAllObjects];
 
-    NSArray *packages = [database_ packages];
+    NSArray *packages([database_ packages]);
     for (Package *package in packages) {
         if ([package half])
             [broken_ addObject:package];
@@ -7241,76 +8063,31 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     if (changes != 0) {
         NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
-        [buttonbar_ setBadgeValue:badge forButton:3];
-        if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
-            [buttonbar_ 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 {
-        [buttonbar_ setBadgeValue:nil forButton:3];
-        if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
-            [buttonbar_ 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;
-    [buttonbar_ setBadgeValue:nil forButton:4];
-
-    [self updateData];
-
-    // XXX: what is this line of code for?
-    if ([packages count] == 0);
-    else if (Loaded_ || ManualRefresh) loaded:
-        [self _loaded];
-    else {
-        Loaded_ = YES;
-
-        if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) {
-            NSTimeInterval interval([update timeIntervalSinceNow]);
-            if (interval <= 0 && interval > -600)
-                goto loaded;
-        }
-
-        [book_ update];
-    }
-}
-
-- (void) _saveConfig {
-    if (Changed_) {
-        _trace();
-        NSString *error(nil);
-        if (NSData *data = [NSPropertyListSerialization dataFromPropertyList:Metadata_ format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error]) {
-            _trace();
-            NSError *error(nil);
-            if (![data writeToFile:@"/var/lib/cydia/metadata.plist" options:NSAtomicWrite error:&error])
-                NSLog(@"failure to save metadata data: %@", error);
-            _trace();
-        } else {
-            NSLog(@"failure to serialize metadata: %@", error);
-            return;
-        }
+    [self _updateData];
 
-        Changed_ = false;
-    }
+    [self refreshIfPossible];
 }
 
 - (void) updateData {
-    [self _saveConfig];
-
-    /* XXX: this is just stupid */
-    if (tag_ != 2 && sections_ != nil)
-        [sections_ reloadData];
-    if (tag_ != 3 && changes_ != nil)
-        [changes_ reloadData];
-    if (tag_ != 5 && search_ != nil)
-        [search_ reloadData];
-
-    [book_ reloadData];
+    [database_ setVisible];
+    [self _updateData];
 }
 
 - (void) update_ {
@@ -7318,13 +8095,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) syncData {
-    FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
+    FILE *file(fopen("/etc/apt/sources.list.d/cydia.list", "w"));
     _assert(file != NULL);
 
-    NSArray *keys = [Sources_ allKeys];
-
-    for (NSString *key in keys) {
-        NSDictionary *source = [Sources_ objectForKey:key];
+    for (NSString *key in [Sources_ allKeys]) {
+        NSDictionary *source([Sources_ objectForKey:key]);
 
         fprintf(file, "%s %s %s\n",
             [[source objectForKey:@"Type"] UTF8String],
@@ -7337,18 +8112,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
     [self _saveConfig];
 
-    [progress_
+    ProgressController *progress = [[[ProgressController alloc] initWithDatabase:database_ delegate:self] autorelease];
+    UINavigationController *navigation = [[[CYNavigationController alloc] initWithRootViewController:progress] autorelease];
+    if (IsWildcat_) [navigation setModalPresentationStyle:UIModalPresentationFormSheet];
+    [container_ presentModalViewController:navigation animated:YES];
+
+    [progress
         detachNewThreadSelector:@selector(update_)
         toTarget:self
         withObject:nil
-        title:CYLocalize("UPDATING_SOURCES")
+        title:UCLocalize("UPDATING_SOURCES")
     ];
 }
 
 - (void) reloadData {
     @synchronized (self) {
-        if (confirm_ == nil)
-            [self _reloadData];
+        [self _reloadData];
     }
 }
 
@@ -7360,25 +8139,23 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         _error->Discard();
 }
 
-- (void) popUpBook:(RVBook *)book {
-    [underlay_ popSubview:book];
-}
-
 - (CGRect) popUpBounds {
-    return [underlay_ bounds];
+    return [[tabbar_ view] bounds];
 }
 
-- (void) perform {
-    [database_ prepare];
+- (bool) perform {
+    if (![database_ prepare])
+        return false;
 
-    confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]];
+    ConfirmationController *page([[[ConfirmationController alloc] initWithDatabase:database_] autorelease]);
+    [page setDelegate:self];
+    id confirm_ = [[CYNavigationController alloc] initWithRootViewController:page];
     [confirm_ setDelegate:self];
 
-    ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
-    [page setDelegate:self];
+    if (IsWildcat_) [confirm_ setModalPresentationStyle:UIModalPresentationFormSheet];
+    [container_ presentModalViewController:confirm_ animated:YES];
 
-    [confirm_ setPage:page];
-    [self popUpBook:confirm_];
+    return true;
 }
 
 - (void) queue {
@@ -7387,9 +8164,18 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     }
 }
 
-- (void) clearPackage:(Package *)package {
+- (void) clearPackage:(Package *)package {
+    @synchronized (self) {
+        [package clear];
+        [self resolve];
+        [self perform];
+    }
+}
+
+- (void) installPackages:(NSArray *)packages {
     @synchronized (self) {
-        [package clear];
+        for (Package *package in packages)
+            [package install];
         [self resolve];
         [self perform];
     }
@@ -7413,427 +8199,251 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) distUpgrade {
     @synchronized (self) {
-        [database_ upgrade];
+        if (![database_ upgrade])
+            return;
         [self perform];
     }
 }
 
-- (void) cancel {
-    [self slideUp:[[[UIActionSheet alloc]
-        initWithTitle:nil
-        buttons:[NSArray arrayWithObjects:CYLocalize("CONTINUE_QUEUING"), CYLocalize("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;
-
-    [progress_
+- (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
         detachNewThreadSelector:@selector(perform)
         toTarget:database_
         withObject:nil
-        title:CYLocalize("RUNNING")
+        title:UCLocalize("RUNNING")
     ];
 }
 
-- (void) bootstrap_ {
-    [database_ update];
-    [database_ upgrade];
-    [database_ prepare];
-    [database_ perform];
+- (void) progressControllerIsComplete:(ProgressController *)progress {
+    [self complete];
 }
 
-/* XXX: replace and localize */
-- (void) bootstrap {
-    [progress_
-        detachNewThreadSelector:@selector(bootstrap_)
-        toTarget:self
-        withObject:nil
-        title:@"Bootstrap Install"
-    ];
-}
+- (void) setPage:(UIViewController *)page {
+    [page setDelegate:self];
 
-- (void) progressViewIsComplete:(ProgressView *)progress {
-    if (confirm_ != nil) {
-        [underlay_ addSubview:overlay_];
-        [confirm_ popFromSuperviewAnimated:NO];
+    UINavigationController *navController = [tabbar_ selectedViewController];
+    [navController setViewControllers:[NSArray arrayWithObject:page] animated:NO];
+    for (UIViewController *page in [tabbar_ viewControllers]) {
+        if (page != navController) [page setViewControllers:nil];
     }
+}
 
-    [self complete];
+- (UIViewController *) _pageForURL:(NSURL *)url withClass:(Class)_class {
+    CYBrowserController *browser = [[[_class alloc] init] autorelease];
+    [browser loadURL:url];
+    return browser;
 }
 
-- (void) setPage:(RVPage *)page {
-    [page resetViewAnimated:NO];
-    [page setDelegate:self];
-    [book_ setPage:page];
+- (SectionsController *) sectionsController {
+    if (sections_ == nil)
+        sections_ = [[SectionsController alloc] initWithDatabase:database_];
+    return sections_;
 }
 
-- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
-    BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
-    [browser loadURL:url];
-    return browser;
+- (ChangesController *) changesController {
+    if (changes_ == nil)
+        changes_ = [[ChangesController alloc] initWithDatabase:database_ delegate:self];
+    return changes_;
+}
+
+- (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_;
 }
 
-- (void) _setHomePage {
-    [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
+- (SearchController *) searchController {
+    if (search_ == nil)
+        search_ = [[SearchController alloc] initWithDatabase:database_];
+    return search_;
 }
 
-- (SectionsView *) sectionsView {
-    if (sections_ == nil)
-        sections_ = [[SectionsView alloc] initWithBook:book_ database:database_];
-    return sections_;
+- (SourceTable *) sourcesController {
+    if (sources_ == nil)
+        sources_ = [[SourceTable alloc] initWithDatabase:database_];
+    return sources_;
+}
+
+- (InstalledController *) installedController {
+    if (installed_ == nil) {
+        installed_ = [[InstalledController alloc] initWithDatabase:database_];
+        if (IsWildcat_) queueDelegate_ = installed_;
+    }
+    return installed_;
 }
 
-- (void) buttonBarItemTapped:(id)sender {
-    unsigned tag = [sender tag];
+- (void) tabBarController:(id)tabBarController didSelectViewController:(UIViewController *)viewController {    
+    int tag = [[viewController tabBarItem] tag];
     if (tag == tag_) {
-        [book_ resetViewAnimated:YES];
+        [[tabbar_ selectedViewController] popToRootViewControllerAnimated:YES];
         return;
-    } else if (tag_ == 2 && tag != 2)
-        [[self sectionsView] resetView];
+    } else if (tag_ == 1) {
+        [[self sectionsController] resetView];
+    }
 
     switch (tag) {
-        case 1: [self _setHomePage]; break;
+        case kCydiaTag: _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;
+        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;
 
-        default: _assert(false);
+        _nodefault
     }
 
     tag_ = tag;
 }
 
-- (void) applicationWillSuspend {
-    [database_ clean];
-    [super applicationWillSuspend];
+- (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) askForSettings {
-    NSString *parenthetical(CYLocalize("PARENTHETICAL"));
-
-    UIActionSheet *role = [[[UIActionSheet alloc]
-        initWithTitle:CYLocalize("WHO_ARE_YOU")
-        buttons:[NSArray arrayWithObjects:
-            [NSString stringWithFormat:parenthetical, CYLocalize("USER"), CYLocalize("USER_EX")],
-            [NSString stringWithFormat:parenthetical, CYLocalize("HACKER"), CYLocalize("HACKER_EX")],
-            [NSString stringWithFormat:parenthetical, CYLocalize("DEVELOPER"), CYLocalize("DEVELOPER_EX")],
-        nil]
-        defaultButtonIndex:-1
-        delegate:self
-        context:@"role"
-    ] autorelease];
-
-    [role setBodyText:CYLocalize("ROLE_EX")];
-    [role popupAlertAnimated:YES];
+- (void) setPackageController:(PackageController *)view {
+    WebThreadLock();
+    [view setPackage:nil];
+#if RecyclePackageViews
+    if ([details_ count] < 3)
+        [details_ addObject:view];
+#endif
+    WebThreadUnlock();
 }
 
-- (void) setPackageView:(PackageView *)view {
-    if (package_ == nil)
-        package_ = [view retain];
+- (PackageController *) _packageController {
+    return [[[PackageController alloc] initWithDatabase:database_] autorelease];
 }
 
-- (PackageView *) packageView {
-    PackageView *view;
+- (PackageController *) packageController {
+#if RecyclePackageViews
+    PackageController *view;
+    size_t count([details_ count]);
 
-    if (package_ == nil)
-        view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
-    else {
-        return package_;
-        view = [package_ autorelease];
-        package_ = nil;
+    if (count == 0) {
+        view = [self _packageController];
+      renew:
+        [details_ addObject:[self _packageController]];
+    } else {
+        view = [[[details_ lastObject] retain] autorelease];
+        [details_ removeLastObject];
+        if (count == 1)
+            goto renew;
     }
 
     return view;
+#else
+    return [self _packageController];
+#endif
 }
 
-- (void) finish {
-    if (hud_ != nil) {
-        [self setStatusBarShowsProgress:NO];
-        [self removeProgressHUD:hud_];
+- (void) cancelAndClear:(bool)clear {
+    @synchronized (self) {
+        if (clear) {
+            /* XXX: clear marks instead of reloading data */
+            /*pkgCacheFile &cache([database_ cache]);
+            for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
+                if (!cache[iterator].Keep()) cache->MarkKeep(iterator, false, false);
+            }
 
-        [hud_ autorelease];
-        hud_ = nil;
+            [self updateData];
+            
+            Queuing_ = false;
+            [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kManageTag] != -1 ? [self indexOfTabWithTag:kManageTag] : [self indexOfTabWithTag:kInstalledTag]] tabBarItem] setBadgeValue:nil];
+            [queueDelegate_ queueStatusDidChange];*/
+            [self reloadData];
+        } else {
+            Queuing_ = true;
 
-        pid_t pid = ExecFork();
-        if (pid == 0) {
-            execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
-            perror("launchctl stop");
+            [[[[tabbar_ viewControllers] objectAtIndex:[self indexOfTabWithTag:kManageTag] != -1 ? [self indexOfTabWithTag:kManageTag] : [self indexOfTabWithTag:kInstalledTag]] tabBarItem] setBadgeValue:UCLocalize("Q_D")];
+            [[tabbar_ selectedViewController] reloadData];
+            
+            [queueDelegate_ queueStatusDidChange];
         }
-
-        return;
     }
-
-    if (Role_ == nil) {
-        [self askForSettings];
-        return;
-    }
-
-    _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,
-            CYLocalize("SECTIONS"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"changes-up.png", kUIButtonBarButtonInfo,
-            @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            CYLocalize("CHANGES"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"manage-up.png", kUIButtonBarButtonInfo,
-            @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            CYLocalize("MANAGE"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-
-        [NSDictionary dictionaryWithObjectsAndKeys:
-            @"buttonBarItemTapped:", kUIButtonBarButtonAction,
-            @"search-up.png", kUIButtonBarButtonInfo,
-            @"search-dn.png", kUIButtonBarButtonSelectedInfo,
-            [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
-            self, kUIButtonBarButtonTarget,
-            CYLocalize("SEARCH"), kUIButtonBarButtonTitle,
-            @"0", kUIButtonBarButtonType,
-        nil],
-    nil];
-
-    buttonbar_ = [[UIToolbar alloc]
-        initInView:overlay_
-        withFrame:CGRectMake(
-            0, screenrect.size.height - ButtonBarHeight_,
-            screenrect.size.width, ButtonBarHeight_
-        )
-        withItemList:buttonitems
-    ];
-
-    [buttonbar_ setDelegate:self];
-    [buttonbar_ setBarStyle:1];
-    [buttonbar_ setButtonBarTrackingMode:2];
-
-    int buttons[5] = {1, 2, 3, 4, 5};
-    [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
-    [buttonbar_ showButtonGroup:0 withDuration:0];
-
-    for (int i = 0; i != 5; ++i)
-        [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
-            i * 64 + 2, 1, 60, ButtonBarHeight_
-        )];
-
-    [buttonbar_ showSelectionForButton:1];
-    [overlay_ addSubview:buttonbar_];
-
-    [UIKeyboard initImplementationNow];
-    CGSize keysize = [UIKeyboard defaultSize];
-    CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
-    keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
-    //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
-    [overlay_ addSubview:keyboard_];
-
-    if (!bootstrap_)
-        [underlay_ addSubview:overlay_];
-
-    [self reloadData];
-
-    [self sectionsView];
-    changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
-    search_ = [[SearchView alloc] initWithBook:book_ database:database_];
-
-    manage_ = (ManageView *) [[self
-        _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
-        withClass:[ManageView class]
-    ] retain];
-
-    [self setPackageView:[self packageView]];
-
-    PrintTimes();
-
-    if (bootstrap_)
-        [self bootstrap];
-    else
-        [self _setHomePage];
 }
 
-- (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) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+    NSString *context([alert context]);
+    
+    if ([context isEqualToString:@"fixhalf"]) {
+        if (button == [alert firstOtherButtonIndex]) {
+            @synchronized (self) {
+                for (Package *broken in broken_) {
+                    [broken remove];
 
-        switch (button) {
-            case 1:
-                clear = false;
-            break;
-
-            case 2:
-                clear = true;
-            break;
-
-            default:
-                _assert(false);
-        }
-
-        [sheet dismiss];
-
-        @synchronized (self) {
-            if (clear)
-                [self _reloadData];
-            else {
-                Queuing_ = true;
-                [buttonbar_ setBadgeValue:CYLocalize("Q_D") forButton:4];
-                [book_ reloadData];
-            }
-
-            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;
-
-            default:
-                _assert(false);
-        }
-
-        [sheet dismiss];
-    } else if ([context isEqualToString:@"role"]) {
-        switch (button) {
-            case 1: Role_ = @"User"; break;
-            case 2: Role_ = @"Hacker"; break;
-            case 3: Role_ = @"Developer"; break;
 
-            default:
-                Role_ = nil;
-                _assert(false);
+                [self resolve];
+                [self perform];
+            }
+        } else if (button == [alert cancelButtonIndex]) {
+            [broken_ removeAllObjects];
+            [self _loaded];
         }
 
-        bool reset = Settings_ != nil;
-
-        Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
-            Role_, @"Role",
-        nil];
-
-        [Metadata_ setObject:Settings_ forKey:@"Settings"];
-
-        Changed_ = true;
-
-        if (reset)
-            [self updateData];
-        else
-            [self finish];
-
-        [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];
 
-            default:
-                _assert(false);
+                [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];
     }
 }
 
-- (void) reorganize { _pooled
-    system("/usr/libexec/cydia/free.sh");
-    [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
+- (void) system:(NSString *)command { _pooled
+    system([command UTF8String]);
+}
+
+- (void) applicationWillSuspend {
+    [database_ clean];
+    [super applicationWillSuspend];
 }
 
 - (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];
 }
 
@@ -7849,9 +8459,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (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;
 }
 
@@ -7861,44 +8473,25 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [window_ setUserInteractionEnabled:YES];
 }
 
-- (void) openMailToURL:(NSURL *)url {
-// XXX: this makes me sad
-#if 0
-    [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
-#else
-    [UIApp openURL:url];// asPanel:YES];
-#endif
-}
-
-- (void) clearFirstResponder {
-    if (id responder = [window_ firstResponder])
-        [responder resignFirstResponder];
-}
-
-- (RVPage *) pageForPackage:(NSString *)name {
+- (UIViewController *) pageForPackage:(NSString *)name {
     if (Package *package = [database_ packageWithName:name]) {
-        PackageView *view([self packageView]);
+        PackageController *view([self packageController]);
         [view setPackage:package];
         return view;
     } else {
-        UIActionSheet *sheet = [[[UIActionSheet alloc]
-            initWithTitle:CYLocalize("CANNOT_LOCATE_PACKAGE")
-            buttons:[NSArray arrayWithObjects:CYLocalize("CLOSE"), nil]
-            defaultButtonIndex:0
-            delegate:self
-            context:@"missing"
-        ] autorelease];
-
-        [sheet setBodyText:[NSString stringWithFormat:CYLocalize("PACKAGE_CANNOT_BE_FOUND"), name]];
-
-        [sheet popupAlertAnimated:YES];
-        return nil;
+        NSURL *url([NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"unknown" ofType:@"html"]]);
+        url = [NSURL URLWithString:[[url absoluteString] stringByAppendingString:[NSString stringWithFormat:@"?%@", name]]];
+        return [self _pageForURL:url withClass:[CYBrowserController class]];
     }
 }
 
-- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
+- (UIViewController *) pageForURL:(NSURL *)url hasTag:(int *)tag {
     if (tag != NULL)
-        tag = 0;
+        *tag = -1;
+
+    NSString *href([url absoluteString]);
+    if ([href hasPrefix:@"apptapp://package/"])
+        return [self pageForPackage:[href substringFromIndex:18]];
 
     NSString *scheme([[url scheme] lowercaseString]);
     if (![scheme isEqualToString:@"cydia"])
@@ -7911,28 +8504,28 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         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:[BrowserView 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:[BrowserView 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;
         }
@@ -7944,141 +8537,143 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 - (void) applicationOpenURL:(NSURL *)url {
     [super applicationOpenURL:url];
     int tag;
-    if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
+    if (UIViewController *page = [self pageForURL:url hasTag:&tag]) {
         [self setPage:page];
-        [buttonbar_ 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];
+}
+
 - (void) applicationDidFinishLaunching:(id)unused {
-    _trace();
+    [CYBrowserController _initialize];
+
+    [NSURLProtocol registerClass:[CydiaURLProtocol class]];
+
     Font12_ = [[UIFont systemFontOfSize:12] retain];
     Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
     Font14_ = [[UIFont systemFontOfSize:14] retain];
     Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
     Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
 
-    tag_ = 1;
+    tag_ = 0;
 
     essential_ = [[NSMutableArray alloc] initWithCapacity:4];
     broken_ = [[NSMutableArray alloc] initWithCapacity:4];
 
-    [NSURLProtocol registerClass:[CydiaURLProtocol class]];
-
-    CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
-    window_ = [[UIWindow alloc] initWithContentRect:screenrect];
+    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 ||
         readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
+        //readlink("/usr/bin", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
+        readlink("/usr/lib/pam", NULL, 0) == -1 && errno == EINVAL ||
         readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
-        readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
-        readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
+        readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL ||
+        //readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL ||
+        false
     ) {
         [self setIdleTimerDisabled:YES];
 
-        hud_ = [[self addProgressHUD] retain];
-        [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
-
+        hud_ = [self addProgressHUD];
+        [hud_ setText:@"Reorganizing:\n\nWill Automatically\nClose When Done"];
         [self setStatusBarShowsProgress:YES];
 
-        [NSThread
-            detachNewThreadSelector:@selector(reorganize)
-            toTarget:self
-            withObject:nil
-        ];
-    } else
-        [self finish];
-}
+        [self yieldToSelector:@selector(system:) withObject:@"/usr/libexec/cydia/free.sh"];
 
-- (void) showKeyboard:(BOOL)show {
-    CGSize keysize = [UIKeyboard defaultSize];
-    CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
-    CGRect keyup = keydown;
-    keyup.origin.y -= keysize.height;
+        [self setStatusBarShowsProgress:NO];
+        [self removeProgressHUD:hud_];
+        hud_ = nil;
 
-    UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
-    [animation setSignificantRectFields:2];
+        if (ExecFork() == 0) {
+            execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
+            perror("launchctl stop");
+        }
 
-    if (show) {
-        [animation setStartFrame:keydown];
-        [animation setEndFrame:keyup];
-        [keyboard_ activate];
-    } else {
-        [animation setStartFrame:keyup];
-        [animation setEndFrame:keydown];
-        [keyboard_ deactivate];
+        return;
     }
 
-    [[UIAnimator sharedAnimator]
-        addAnimations:[NSArray arrayWithObjects:animation, nil]
-        withDuration:KeyboardTime_
-        start:YES
+    if (Role_ == nil)
+        [self showSettings];
+
+    _trace();
+
+    NSMutableArray *controllers = [NSMutableArray array];
+    [controllers addObject:[[CYNavigationController alloc] initWithDatabase:database_]];
+    [controllers addObject:[[CYNavigationController alloc] initWithDatabase:database_]];
+    [controllers addObject:[[CYNavigationController alloc] initWithDatabase:database_]];
+    if (IsWildcat_) [controllers addObject:[[CYNavigationController alloc] initWithDatabase:database_]];
+    [controllers addObject:[[CYNavigationController alloc] initWithDatabase:database_]];
+    [controllers addObject:[[CYNavigationController alloc] initWithDatabase:database_]];
+
+    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
     ];
-}
+    
+    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];
+    }
+    
+    for (int i = 0; i < [items count]; i++) {
+        [[controllers objectAtIndex:i] setTabBarItem:[items objectAtIndex:i]];
+    }
 
-- (void) slideUp:(UIActionSheet *)alert {
-    if (Advanced_)
-        [alert presentSheetFromButtonBar:buttonbar_];
-    else
-        [alert presentSheetInView:overlay_];
-}
+    tabbar_ = [[CYTabBarController alloc] initWithDatabase:database_];
+    [tabbar_ setViewControllers:controllers];
+    [tabbar_ setDelegate:self];
+    [tabbar_ setSelectedIndex:0];
 
-@end
+    container_ = [[CYContainer alloc] initWithDatabase:database_];
+    [container_ setUpdateDelegate:self];
+    [container_ setRootController:tabbar_];
+    [window_ addSubview:[container_ view]];
+    [[tabbar_ view] setFrame:CGRectMake(0, -20.0f, [window_ bounds].size.width, [window_ bounds].size.height)];
 
-void AddPreferences(NSString *plist) { _pooled
-    NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
-    _assert(settings != NULL);
-    NSMutableArray *items = [settings objectForKey:@"items"];
+    [UIKeyboard initImplementationNow];
 
-    bool cydia(false);
+    [self reloadData];
 
-    for (NSMutableDictionary *item in items) {
-        NSString *label = [item objectForKey:@"label"];
-        if (label != nil && [label isEqualToString:@"Cydia"]) {
-            cydia = true;
-            break;
-        }
-    }
+#if RecyclePackageViews
+    details_ = [[NSMutableArray alloc] initWithCapacity:4];
+    [details_ addObject:[self _packageController]];
+    [details_ addObject:[self _packageController]];
+#endif
 
-    if (!cydia) {
-        for (size_t i(0); i != [items count]; ++i) {
-            NSDictionary *item([items objectAtIndex:i]);
-            NSString *label = [item objectForKey:@"label"];
-            if (label != nil && [label isEqualToString:@"General"]) {
-                [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
-                    @"CydiaSettings", @"bundle",
-                    @"PSLinkCell", @"cell",
-                    [NSNumber numberWithBool:YES], @"hasIcon",
-                    [NSNumber numberWithBool:YES], @"isController",
-                    @"Cydia", @"label",
-                nil] atIndex:(i + 1)];
+    PrintTimes();
 
-                break;
-            }
-        }
+    _setHomePage(self);
+}
 
-        _assert([settings writeToFile:plist atomically:YES] == YES);
+- (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {
+    if (item != nil && IsWildcat_) {
+        [sheet showFromBarButtonItem:item animated:YES];
+    } else {
+        [sheet showInView:window_];
     }
 }
 
+@end
+
 /*IMP alloc_;
 id Alloc_(id self, SEL selector) {
     id object = alloc_(self, selector);
@@ -8095,20 +8690,45 @@ id Dealloc_(id self, SEL selector) {
 
 Class $WebDefaultUIKitDelegate;
 
-void (*_UIWebDocumentView$_setUIKitDelegate$)(UIWebDocumentView *, SEL, id);
-
-void $UIWebDocumentView$_setUIKitDelegate$(UIWebDocumentView *self, SEL sel, id delegate) {
+MSHook(void, UIWebDocumentView$_setUIKitDelegate$, UIWebDocumentView *self, SEL _cmd, id delegate) {
     if (delegate == nil && $WebDefaultUIKitDelegate != nil)
         delegate = [$WebDefaultUIKitDelegate sharedUIKitDelegate];
-    return _UIWebDocumentView$_setUIKitDelegate$(self, sel, delegate);
+    return _UIWebDocumentView$_setUIKitDelegate$(self, _cmd, delegate);
+}
+
+static NSNumber *shouldPlayKeyboardSounds;
+
+Class $UIHardware;
+
+MSHook(void, UIHardware$_playSystemSound$, Class self, SEL _cmd, int soundIndex) {
+    switch (soundIndex) {
+        case 1104: // Keyboard Button Clicked
+        case 1105: // Keyboard Delete Repeated
+            if (!shouldPlayKeyboardSounds) {
+                NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.preferences.sounds.plist"];
+                shouldPlayKeyboardSounds = [[dict objectForKey:@"keyboard"] ?: (id)kCFBooleanTrue retain];
+                [dict release];
+            }
+            if (![shouldPlayKeyboardSounds boolValue])
+                break;
+        default:
+            _UIHardware$_playSystemSound$(self, _cmd, soundIndex);
+    }
 }
 
 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 {{{ */
+    class_addMethod(objc_getClass("WebScriptObject"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &WebScriptObject$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
     class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
 
     $WebDefaultUIKitDelegate = objc_getClass("WebDefaultUIKitDelegate");
@@ -8117,24 +8737,34 @@ 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();
     Languages_ = [NSLocale preferredLanguages];
     //CFStringRef locale(CFLocaleGetIdentifier(Locale_));
     //NSLog(@"%@", [Languages_ description]);
+
     const char *lang;
     if (Languages_ == nil || [Languages_ count] == 0)
+        // XXX: consider just setting to C and then falling through?
         lang = NULL;
-    else
+    else {
         lang = [[Languages_ objectAtIndex:0] UTF8String];
-    setenv("LANG", lang, true);
+        setenv("LANG", lang, true);
+    }
+
     //std::setlocale(LC_ALL, lang);
     NSLog(@"Setting Language: %s", lang);
     /* }}} */
 
-    // XXX: apr_app_initialize?
-    apr_initialize();
+    apr_app_initialize(&argc, const_cast<const char * const **>(&argv), NULL);
 
     /* Parse Arguments {{{ */
     bool substrate(false);
@@ -8153,24 +8783,16 @@ int main(int argc, char *argv[]) { _pooled
             }
 
         for (int argi(1); argi != arge; ++argi)
-            if (strcmp(args[argi], "--bootstrap") == 0)
-                bootstrap_ = true;
-            else if (strcmp(args[argi], "--substrate") == 0)
+            if (strcmp(args[argi], "--substrate") == 0)
                 substrate = true;
             else
                 fprintf(stderr, "unknown argument: %s\n", args[argi]);
     }
     /* }}} */
 
-    {
-        NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
-        if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
-            if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
-                Sounds_Keyboard_ = [keyboard boolValue];
-    }
-
     App_ = [[NSBundle mainBundle] bundlePath];
     Home_ = NSHomeDirectory();
+    Advanced_ = YES;
 
     setuid(0);
     setgid(0);
@@ -8183,6 +8805,7 @@ int main(int argc, char *argv[]) { _pooled
     dealloc_ = dealloc->method_imp;
     dealloc->method_imp = (IMP) &Dealloc_;*/
 
+    /* System Information {{{ */
     size_t size;
 
     int maxproc;
@@ -8195,6 +8818,13 @@ int main(int argc, char *argv[]) { _pooled
             perror("sysctlbyname(\"kern.maxproc\", #)");
     }
 
+    sysctlbyname("kern.osversion", NULL, &size, NULL, 0);
+    char *osversion = new char[size];
+    if (sysctlbyname("kern.osversion", osversion, &size, NULL, 0) == -1)
+        perror("sysctlbyname(\"kern.osversion\", ?)");
+    else
+        System_ = [NSString stringWithUTF8String:osversion];
+
     sysctlbyname("hw.machine", NULL, &size, NULL, 0);
     char *machine = new char[size];
     if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
@@ -8202,6 +8832,29 @@ int main(int argc, char *argv[]) { _pooled
     else
         Machine_ = machine;
 
+    if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice")) {
+        if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
+            if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
+                SerialNumber_ = [NSString stringWithString:(NSString *)serial];
+                CFRelease(serial);
+            }
+
+            if (CFTypeRef ecid = IORegistryEntrySearchCFProperty(service, kIODeviceTreePlane, CFSTR("unique-chip-id"), kCFAllocatorDefault, kIORegistryIterateRecursively)) {
+                NSData *data((NSData *) ecid);
+                size_t length([data length]);
+                uint8_t bytes[length];
+                [data getBytes:bytes];
+                char string[length * 2 + 1];
+                for (size_t i(0); i != length; ++i)
+                    sprintf(string + i * 2, "%.2X", bytes[length - i - 1]);
+                ChipID_ = [NSString stringWithUTF8String:string];
+                CFRelease(ecid);
+            }
+
+            IOObjectRelease(service);
+        }
+    }
+
     UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
 
     if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"])
@@ -8210,10 +8863,7 @@ int main(int argc, char *argv[]) { _pooled
         Product_ = [info objectForKey:@"SafariProductVersion"];
         Safari_ = [info objectForKey:@"CFBundleVersion"];
     }
-
-    /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
-    AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
-
+    /* }}} */
     /* Load Database {{{ */
     _trace();
     Metadata_ = [[[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"] autorelease];
@@ -8229,6 +8879,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)
@@ -8254,12 +8906,22 @@ int main(int argc, char *argv[]) { _pooled
     Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
 #endif
 
+    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);*/
 
-    if (access("/User", F_OK) != 0) {
+    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 || version < 2) {
+      firmware:
         _trace();
         system("/usr/libexec/cydia/firmware.sh");
         _trace();
@@ -8279,14 +8941,15 @@ int main(int argc, char *argv[]) { _pooled
             _assert(errno == ENOENT);
     }
 
+    /* APT Initialization {{{ */
     _assert(pkgInitConfig(*_config));
     _assert(pkgInitSystem(*_config, _system));
 
     if (lang != NULL)
         _config->Set("APT::Acquire::Translation", lang);
     _config->Set("Acquire::http::Timeout", 15);
-    _config->Set("Acquire::http::MaxParallel", 4);
-
+    _config->Set("Acquire::http::MaxParallel", 3);
+    /* }}} */
     /* Color Choices {{{ */
     space_ = CGColorSpaceCreateDeviceRGB();
 
@@ -8299,28 +8962,23 @@ int main(int argc, char *argv[]) { _pooled
     Green_.Set(space_, 0.0, 0.5, 0.0, 1.0);
     Purple_.Set(space_, 0.0, 0.0, 0.7, 1.0);
     Purplish_.Set(space_, 0.4, 0.4, 0.8, 1.0);
-    /*Purple_.Set(space_, 1.0, 0.3, 0.0, 1.0);
-    Purplish_.Set(space_, 1.0, 0.6, 0.4, 1.0); ORANGE */
-    /*Purple_.Set(space_, 1.0, 0.5, 0.0, 1.0);
-    Purplish_.Set(space_, 1.0, 0.7, 0.2, 1.0); ORANGISH */
-    /*Purple_.Set(space_, 0.5, 0.0, 0.7, 1.0);
-    Purplish_.Set(space_, 0.7, 0.4, 0.8, 1.0); PURPLE */
-
-//.93
+
     InstallingColor_ = [UIColor colorWithRed:0.88f green:1.00f blue:0.88f alpha:1.00f];
     RemovingColor_ = [UIColor colorWithRed:1.00f green:0.88f blue:0.88f alpha:1.00f];
     /* }}}*/
-
-    Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
-
     /* UIKit Configuration {{{ */
     void (*$GSFontSetUseLegacyFontMetrics)(BOOL)(reinterpret_cast<void (*)(BOOL)>(dlsym(RTLD_DEFAULT, "GSFontSetUseLegacyFontMetrics")));
     if ($GSFontSetUseLegacyFontMetrics != NULL)
         $GSFontSetUseLegacyFontMetrics(YES);
 
-    UIKeyboardDisableAutomaticAppearance();
+    // XXX: I have a feeling this was important
+    //UIKeyboardDisableAutomaticAppearance();
     /* }}} */
 
+    Colon_ = UCLocalize("COLON_DELIMITED");
+    Error_ = UCLocalize("ERROR");
+    Warning_ = UCLocalize("WARNING");
+
     _trace();
     int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
 
@@ -8328,4 +8986,4 @@ int main(int argc, char *argv[]) { _pooled
     CFRelease(Locale_);
 
     return value;
-}
+}
\ No newline at end of file