]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
Allow profiling usage of Database::sections_ in -[Database mappedSectionForPointer:].
[cydia.git] / MobileCydia.mm
index 6121e6eb6d3bac72ec5f8c4d7af6c7d3f6cf6bbf..b7050fc6d3dafbded0ce1e5468f64f2a1b8bd902 100644 (file)
 #include <sys/sysctl.h>
 #include <sys/param.h>
 #include <sys/mount.h>
+#include <sys/reboot.h>
 
 #include <fcntl.h>
 #include <notify.h>
@@ -1354,6 +1355,8 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
     int cydiafd_;
     int statusfd_;
     FILE *input_;
+
+    std::map<const char *, _H<NSString> > sections_;
 }
 
 + (Database *) sharedInstance;
@@ -1387,6 +1390,9 @@ typedef std::map< unsigned long, _H<Source> > SourceMap;
 
 - (void) setDelegate:(id)delegate;
 - (Source *) getSource:(pkgCache::PkgFileIterator)file;
+
+- (NSString *) mappedSectionForPointer:(const char *)pointer;
+
 @end
 /* }}} */
 /* Delegate Helpers {{{ */
@@ -1490,7 +1496,7 @@ static void PackageImport(const void *key, const void *value, void *context) {
     NSDictionary *package((NSDictionary *) value);
 
     if (NSNumber *subscribed = [package objectForKey:@"IsSubscribed"])
-        if ([subscribed boolValue])
+        if ([subscribed boolValue] && !metadata->subscribed_)
             metadata->subscribed_ = true;
 
     if (NSDate *date = [package objectForKey:@"FirstSeen"]) {
@@ -1499,22 +1505,12 @@ static void PackageImport(const void *key, const void *value, void *context) {
             metadata->first_ = time;
     }
 
-    bool versioned(false);
+    NSDate *date([package objectForKey:@"LastSeen"]);
+    NSString *version([package objectForKey:@"LastVersion"]);
 
-    if (NSDate *date = [package objectForKey:@"LastSeen"]) {
+    if (date != nil && version != nil) {
         time_t time([date timeIntervalSince1970]);
-        if (metadata->last_ < time || metadata->last_ == 0) {
-            metadata->last_ = time;
-            versioned = true;
-        }
-    } else if (metadata->last_ == 0) {
-        metadata->last_ = metadata->first_;
-        if (metadata->version_[0] == '\0')
-            versioned = true;
-    }
-
-    if (versioned)
-        if (NSString *version = [package objectForKey:@"LastVersion"])
+        if (metadata->last_ < time || metadata->last_ == 0)
             if (CFStringGetCString((CFStringRef) version, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
                 size_t length(strlen(buffer));
                 uint16_t vhash(hashlittle(buffer, length));
@@ -1524,7 +1520,10 @@ static void PackageImport(const void *key, const void *value, void *context) {
 
                 strncpy(metadata->version_, latest, sizeof(metadata->version_));
                 metadata->vhash_ = vhash;
+
+                metadata->last_ = time;
             }
+    }
 }
 // }}}
 
@@ -1831,7 +1830,8 @@ struct ParsedPackage {
 };
 
 @interface Package : NSObject {
-    uint32_t era_ : 29;
+    uint32_t era_ : 26;
+    uint32_t role_ : 3;
     uint32_t essential_ : 1;
     uint32_t obsolete_ : 1;
     uint32_t ignored_ : 1;
@@ -1850,7 +1850,7 @@ struct ParsedPackage {
     CYString latest_;
     CYString installed_;
 
-    CYString section_;
+    const char *section_;
     _transient NSString *section$_;
 
     Source *source_;
@@ -1859,7 +1859,6 @@ struct ParsedPackage {
     ParsedPackage *parsed_;
 
     NSMutableArray *tags_;
-    NSString *role_;
 }
 
 - (Package *) initWithVersion:(pkgCache::VerIterator)version withZone:(NSZone *)zone inPool:(apr_pool_t *)pool database:(Database *)database;
@@ -1921,7 +1920,6 @@ struct ParsedPackage {
 - (NSArray *) applications;
 
 - (Source *) source;
-- (NSString *) role;
 
 - (BOOL) matches:(NSString *)text;
 
@@ -2082,15 +2080,10 @@ struct PackageNameOrdering :
 - (void) dealloc {
     if (parsed_ != NULL)
         delete parsed_;
-
     if (source_ != nil)
         [source_ release];
-
     if (tags_ != nil)
         [tags_ release];
-    if (role_ != nil)
-        [role_ release];
-
     [super dealloc];
 }
 
@@ -2224,8 +2217,18 @@ struct PackageNameOrdering :
                     const char *name(tag.Name());
                     [tags_ addObject:[(NSString *)CYStringCreate(name) autorelease]];
 
-                    if (role_ == nil && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/)
-                        role_ = (NSString *) CYStringCreate(name + 6);
+                    if (role_ == 0 && strncmp(name, "role::", 6) == 0 /*&& strcmp(name, "role::leaper") != 0*/) {
+                        if (strcmp(name + 6, "enduser") == 0)
+                            role_ = 1;
+                        else if (strcmp(name + 6, "hacker") == 0)
+                            role_ = 2;
+                        else if (strcmp(name + 6, "developer") == 0)
+                            role_ = 3;
+                        else if (strcmp(name + 6, "cydia") == 0)
+                            role_ = 7;
+                        else
+                            role_ = 4;
+                    }
 
                     if (strncmp(name, "cydia::", 7) == 0) {
                         if (strcmp(name + 7, "essential") == 0)
@@ -2264,19 +2267,16 @@ struct PackageNameOrdering :
             if (metadata->first_ == 0)
                 metadata->first_ = now_;
 
-            if (metadata->last_ == 0)
-                metadata->last_ = metadata->first_;
-
             if (metadata->vhash_ != vhash || strncmp(metadata->version_, latest, sizeof(metadata->version_)) != 0) {
-                if (metadata->version_[0] != '\0')
-                    metadata->last_ = now_;
                 strncpy(metadata->version_, latest, sizeof(metadata->version_));
                 metadata->vhash_ = vhash;
-            }
+                metadata->last_ = now_;
+            } else if (metadata->last_ == 0)
+                metadata->last_ = metadata->first_;
         _end
 
         _profile(Package$initWithVersion$Section)
-            section_.set(NULL, iterator.Section());
+            section_ = iterator.Section();
         _end
 
         _profile(Package$initWithVersion$Flags)
@@ -2324,13 +2324,11 @@ struct PackageNameOrdering :
 
 - (NSString *) section {
     if (section$_ == nil) {
-        if (section_.empty())
+        if (section_ == NULL)
             return nil;
 
-        _profile(Package$section)
-            std::replace(section_.data(), section_.data() + section_.size(), '_', ' ');
-            NSString *name(section_);
-            section$_ = [SectionMap_ objectForKey:name] ?: name;
+        _profile(Package$section$mappedSectionForPointer)
+            section$_ = [database_ mappedSectionForPointer:section_];
         _end
     } return section$_;
 }
@@ -2480,12 +2478,12 @@ struct PackageNameOrdering :
 
 - (BOOL) unfiltered {
     _profile(Package$unfiltered$obsolete)
-        if (obsolete_)
+        if (_unlikely(obsolete_))
             return false;
     _end
 
     _profile(Package$unfiltered$hasSupportingRole)
-        if (![self hasSupportingRole])
+        if (_unlikely(![self hasSupportingRole]))
             return false;
     _end
 
@@ -2496,7 +2494,11 @@ struct PackageNameOrdering :
     if (![self unfiltered])
         return false;
 
-    NSString *section([self section]);
+    NSString *section;
+
+    _profile(Package$visible$section)
+        section = [self section];
+    _end
 
     _profile(Package$visible$isSectionVisible)
         if (section != nil && !isSectionVisible(section))
@@ -2719,10 +2721,6 @@ struct PackageNameOrdering :
     return source_ == (Source *) [NSNull null] ? nil : source_;
 }
 
-- (NSString *) role {
-    return role_;
-}
-
 - (BOOL) matches:(NSString *)text {
     if (text == nil)
         return NO;
@@ -2745,17 +2743,17 @@ struct PackageNameOrdering :
 }
 
 - (bool) hasSupportingRole {
-    if (role_ == nil)
+    if (role_ == 0)
         return true;
-    if ([role_ isEqualToString:@"enduser"])
+    if (role_ == 1)
         return true;
     if ([Role_ isEqualToString:@"User"])
         return false;
-    if ([role_ isEqualToString:@"hacker"])
+    if (role_ == 2)
         return true;
     if ([Role_ isEqualToString:@"Hacker"])
         return false;
-    if ([role_ isEqualToString:@"developer"])
+    if (role_ == 3)
         return true;
     if ([Role_ isEqualToString:@"Developer"])
         return false;
@@ -2877,7 +2875,7 @@ struct PackageNameOrdering :
 }
 
 - (bool) isInstalledAndUnfiltered:(NSNumber *)number {
-    return ![self uninstalled] && (![number boolValue] && ![role_ isEqualToString:@"cydia"] || [self unfiltered]);
+    return ![self uninstalled] && (![number boolValue] && role_ != 7 || [self unfiltered]);
 }
 
 - (bool) isVisibleInSection:(NSString *)name {
@@ -3507,7 +3505,9 @@ static NSString *Warning_;
 
 - (void) configure {
     NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
+    _trace();
     system([dpkg UTF8String]);
+    _trace();
 }
 
 - (bool) clean {
@@ -3684,6 +3684,37 @@ static NSString *Warning_;
     return i == sources_.end() ? nil : i->second;
 }
 
+- (NSString *) mappedSectionForPointer:(const char *)section {
+    _H<NSString> *mapped;
+
+    _profile(Database$mappedSectionForPointer$Cache)
+        mapped = &sections_[section];
+    _end
+
+    if (*mapped == NULL) {
+        size_t length(strlen(section));
+        char spaced[length + 1];
+
+        _profile(Database$mappedSectionForPointer$Replace)
+            for (size_t index(0); index != length; ++index)
+                spaced[index] = section[index] == '_' ? ' ' : section[index];
+            spaced[length] = '\0';
+        _end
+
+        NSString *string;
+
+        _profile(Database$mappedSectionForPointer$stringWithUTF8String)
+            string = [NSString stringWithUTF8String:spaced];
+        _end
+
+        _profile(Database$mappedSectionForPointer$Map)
+            string = [SectionMap_ objectForKey:string] ?: string;
+        _end
+
+        *mapped = string;
+    } return *mapped;
+}
+
 @end
 /* }}} */
 
@@ -3940,6 +3971,71 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 
+/* @ Loading... Indicator {{{ */
+@interface CYLoadingIndicator : UIView {
+    UIActivityIndicatorView *spinner_;
+    UILabel *label_;
+    UIView *container_;
+}
+
+@property (readonly, nonatomic) UILabel *label;
+@property (readonly, nonatomic) UIActivityIndicatorView *activityIndicatorView;
+
+@end
+
+@implementation CYLoadingIndicator
+
+- (id)initWithFrame:(CGRect)frame {
+    if ((self = [super initWithFrame:frame])) {
+        container_ = [[[UIView alloc] init] autorelease];
+        [container_ setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin];
+
+        spinner_ = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
+        [spinner_ startAnimating];
+        [container_ addSubview:spinner_];
+
+        label_ = [[[UILabel alloc] init] autorelease];
+        [label_ setFont:[UIFont boldSystemFontOfSize:15.0f]];
+        [label_ setBackgroundColor:[UIColor clearColor]];
+        [label_ setTextColor:[UIColor blackColor]];
+        [label_ setShadowColor:[UIColor whiteColor]];
+        [label_ setShadowOffset:CGSizeMake(0, 1)];
+        [label_ setText:[NSString stringWithFormat:Elision_, UCLocalize("LOADING"), nil]];
+        [container_ addSubview:label_];
+
+        CGSize viewsize = frame.size;
+        CGSize spinnersize = [spinner_ bounds].size;
+        CGSize textsize = [[label_ text] sizeWithFont:[label_ font]];
+        float bothwidth = spinnersize.width + textsize.width + 5.0f;
+
+        CGRect containrect = {
+            CGPointMake(floorf((viewsize.width / 2) - (bothwidth / 2)), floorf((viewsize.height / 2) - (spinnersize.height / 2))),
+            CGSizeMake(bothwidth, spinnersize.height)
+        };
+        CGRect textrect = {
+            CGPointMake(spinnersize.width + 5.0f, floorf((spinnersize.height / 2) - (textsize.height / 2))),
+            textsize
+        };
+        CGRect spinrect = {
+            CGPointZero,
+            spinnersize
+        };
+
+        [container_ setFrame:containrect];
+        [spinner_ setFrame:spinrect];
+        [label_ setFrame:textrect];
+        [self addSubview:container_];
+    }
+
+    return self;
+}
+
+- (UILabel *)label { return label_; }
+- (UIActivityIndicatorView *)activityIndicatorView { return spinner_; }
+
+@end
+/* }}} */
+
 /* Cydia Browser Controller {{{ */
 @interface CYBrowserController : BrowserController {
     CydiaObject *cydia_;
@@ -4441,18 +4537,24 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         break;
 
         case 2:
-            system("launchctl stop com.apple.SpringBoard");
-        break;
+            _trace();
+            goto reload;
 
         case 3:
-            system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
+            _trace();
+            goto reload;
+
+        reload:
+            system("/usr/bin/sbreload");
+            _trace();
         break;
 
         case 4:
+            _trace();
             if (void (*SBReboot)(mach_port_t) = reinterpret_cast<void (*)(mach_port_t)>(dlsym(RTLD_DEFAULT, "SBReboot")))
                 SBReboot(SBSSpringBoardServerPort());
             else
-                system("reboot");
+                reboot2(RB_AUTOBOOT);
         break;
     }
 }
@@ -4501,7 +4603,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         case 4: [close_ setTitle:UCLocalize("REBOOT_DEVICE")]; break;
     }
 
+    _trace();
     system("su -c /usr/bin/uicache mobile");
+    _trace();
 
     UpdateExternalStatus(Finish_ == 0 ? 2 : 0);
 
@@ -5078,7 +5182,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) drawContentRect:(CGRect)rect {
-    bool highlighted(highlighted_);
+    bool highlighted(highlighted_ && !editing_);
 
     [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
 
@@ -5340,7 +5444,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) reloadButtonClicked {
-    // Don't reload a package view by clicking the button.
+    // Don't reload a commerical package by tapping the loading button,
+    // but if it's not an Install button, we should forward it on.
+    if (![package_ uninstalled])
+        [self _customButtonClicked];
 }
 
 - (void) applyLoadingTitle {
@@ -5469,13 +5576,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     [super dealloc];
 }
 
++ (BOOL) hasIndexedCollation {
+    return NO; // XXX: objc_getClass("UILocalizedIndexedCollation") != nil;
+}
+
 - (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
     NSInteger count([sections_ count]);
     return count == 0 ? 1 : count;
 }
 
 - (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
-    if ([sections_ count] == 0)
+    if ([sections_ count] == 0 || [[sections_ objectAtIndex:section] count] == 0)
         return nil;
     return [[sections_ objectAtIndex:section] name];
 }
@@ -5521,10 +5632,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView {
+    // XXX: is 20 the most optimal number here?
     return [packages_ count] > 20 ? index_ : nil;
 }
 
-- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
+- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
+    if ([[self class] hasIndexedCollation]) {
+        return [[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionForSectionIndexTitleAtIndex:index];
+    }
+
     return index;
 }
 
@@ -5535,7 +5651,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
         target_ = target;
         action_ = action;
 
-        index_ = [[NSMutableArray alloc] initWithCapacity:32];
+        index_ = [[self class] hasIndexedCollation]
+            ? [[[objc_getClass("UILocalizedIndexedCollation") currentCollation] sectionIndexTitles] retain]
+            : [[NSMutableArray alloc] initWithCapacity:32];
         indices_ = [[NSMutableDictionary alloc] initWithCapacity:32];
 
         packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
@@ -5572,37 +5690,70 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
                 [packages_ addObject:package];
     _end
 
-    [index_ removeAllObjects];
     [indices_ removeAllObjects];
 
     Section *section = nil;
 
-    _profile(PackageTable$reloadData$Section)
-        for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
-            Package *package;
-            unichar index;
+    if ([[self class] hasIndexedCollation]) {
+        id collation = [objc_getClass("UILocalizedIndexedCollation") currentCollation];
+        NSArray *titles = [collation sectionIndexTitles];
+        int secidx = -1;
 
-            _profile(PackageTable$reloadData$Section$Package)
-                package = [packages_ objectAtIndex:offset];
-                index = [package index];
-            _end
+        _profile(PackageTable$reloadData$Section)
+            for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
+                Package *package;
+                int index;
 
-            if (section == nil || [section index] != index) {
-                _profile(PackageTable$reloadData$Section$Allocate)
-                    section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
+                _profile(PackageTable$reloadData$Section$Package)
+                    package = [packages_ objectAtIndex:offset];
+                    index = [collation sectionForObject:package collationStringSelector:@selector(name)];
                 _end
 
-                [index_ addObject:[section name]];
-                //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index];
+                while (secidx < index) {
+                    secidx += 1;
 
-                _profile(PackageTable$reloadData$Section$Add)
-                    [sections_ addObject:section];
-                _end
+                    _profile(PackageTable$reloadData$Section$Allocate)
+                        section = [[[Section alloc] initWithName:[titles objectAtIndex:secidx] row:offset localize:NO] autorelease];
+                    _end
+
+                    _profile(PackageTable$reloadData$Section$Add)
+                        [sections_ addObject:section];
+                    _end
+                }
+
+                [section addToCount];
             }
+        _end
+    } else {
+        [index_ removeAllObjects];
 
-            [section addToCount];
-        }
-    _end
+        _profile(PackageTable$reloadData$Section)
+            for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
+                Package *package;
+                unichar index;
+
+                _profile(PackageTable$reloadData$Section$Package)
+                    package = [packages_ objectAtIndex:offset];
+                    index = [package index];
+                _end
+
+                if (section == nil || [section index] != index) {
+                    _profile(PackageTable$reloadData$Section$Allocate)
+                        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
+                }
+
+                [section addToCount];
+            }
+        _end
+    }
 
     _profile(PackageTable$reloadData$List)
         [list_ reloadData];
@@ -5753,25 +5904,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 /* }}} */
 
-/* Add Source Controller {{{ */
-@interface AddSourceController : CYViewController {
-    _transient Database *database_;
-}
-
-- (id) initWithDatabase:(Database *)database;
-
-@end
-
-@implementation AddSourceController
-
-- (id) initWithDatabase:(Database *)database {
-    if ((self = [super init]) != nil) {
-        database_ = database;
-    } return self;
-}
-
-@end
-/* }}} */
 /* Source Cell {{{ */
 @interface SourceCell : CYTableViewCell <
     ContentDelegate
@@ -5862,7 +5994,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 @end
 /* }}} */
 /* Source Table {{{ */
-@interface SourceTable : CYViewController <
+@interface SourceController : CYViewController <
     UITableViewDataSource,
     UITableViewDelegate
 > {
@@ -5890,7 +6022,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @end
 
-@implementation SourceTable
+@implementation SourceController
 
 - (void) _releaseConnection:(NSURLConnection *)connection {
     if (connection != nil) {
@@ -6407,6 +6539,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 @implementation HomeController
 
++ (BOOL)shouldHideNavigationBar {
+    return NO;
+}
+
 - (void) _setMoreHeaders:(NSMutableURLRequest *)request {
     [super _setMoreHeaders:request];
 
@@ -6437,12 +6573,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) viewWillAppear:(BOOL)animated {
     [super viewWillAppear:animated];
-    //[[self navigationController] setNavigationBarHidden:YES animated:animated];
+
+    if ([[self class] shouldHideNavigationBar])
+        [[self navigationController] setNavigationBarHidden:YES animated:animated];
 }
 
 - (void) viewWillDisappear:(BOOL)animated {
     [super viewWillDisappear:animated];
-    //[[self navigationController] setNavigationBarHidden:NO animated:animated];
+
+    if ([[self class] shouldHideNavigationBar])
+        [[self navigationController] setNavigationBarHidden:NO animated:animated];
 }
 
 - (id) init {
@@ -6930,6 +7070,8 @@ freeing the view controllers on tab change */
     // Inherit autorotation settings for modal parents.
     if ([self parentViewController] && [[self parentViewController] modalViewController] == self) {
         return [[self parentViewController] shouldAutorotateToInterfaceOrientation:orientation];
+    } else if ([self parentViewController]) {
+        return [[self parentViewController] shouldAutorotateToInterfaceOrientation:orientation];
     } else {
         return [super shouldAutorotateToInterfaceOrientation:orientation];
     }
@@ -7061,7 +7203,7 @@ freeing the view controllers on tab change */
 /* }}} */
 
 /* Sections Controller {{{ */
-@interface SectionsController : CYViewController <
+@interface CYSectionsController : CYViewController <
     UITableViewDataSource,
     UITableViewDelegate
 > {
@@ -7081,7 +7223,7 @@ freeing the view controllers on tab change */
 
 @end
 
-@implementation SectionsController
+@implementation CYSectionsController
 
 - (void) dealloc {
     [list_ setDataSource:nil];
@@ -7094,11 +7236,27 @@ freeing the view controllers on tab change */
     [super dealloc];
 }
 
+- (void) setEditing:(BOOL)editing {
+    if ((editing_ = editing))
+        [list_ reloadData];
+    else
+        [delegate_ updateData];
+
+    [[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];
+}
+
 - (void) viewDidAppear:(BOOL)animated {
     [super viewDidAppear:animated];
     [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
 }
 
+- (void) viewWillDisappear:(BOOL)animated {
+    [super viewWillDisappear:animated];
+    if (editing_) [self setEditing:NO];
+}
+
 - (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath {
     Section *section = (editing_ ? [sections_ objectAtIndex:[indexPath row]] : ([indexPath row] == 0 ? nil : [filtered_ objectAtIndex:([indexPath row] - 1)]));
     return section;
@@ -7247,15 +7405,8 @@ freeing the view controllers on tab change */
         [self editButtonClicked];
 }
 
-- (void) editButtonClicked {
-    if ((editing_ = !editing_))
-        [list_ reloadData];
-    else
-        [delegate_ updateData];
-
-    [[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];
+- (void)editButtonClicked {
+    [self setEditing:!editing_];
 }
 
 - (UIView *) accessoryView {
@@ -8012,11 +8163,11 @@ typedef enum {
     unsigned locked_;
     unsigned activity_;
 
-    SectionsController *sections_;
+    CYSectionsController *sections_;
     ChangesController *changes_;
     ManageController *manage_;
     SearchController *search_;
-    SourceTable *sources_;
+    SourceController *sources_;
     InstalledController *installed_;
     id queueDelegate_;
 
@@ -8048,10 +8199,6 @@ static _finline void _setHomePage(Cydia *self) {
     return [tabbar_ updating];
 }
 
-- (UIView *) rotatingContentViewForWindow:(UIWindow *)window {
-    return window_;
-}
-
 - (void) _loaded {
     if ([broken_ count] != 0) {
         int count = [broken_ count];
@@ -8107,15 +8254,25 @@ static _finline void _setHomePage(Cydia *self) {
 - (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];
+    NSMutableSet *tabs([[[NSMutableSet alloc] initWithCapacity:10] autorelease]);
+
+    [tabs addObject:[tabbar_ selectedViewController]];
 
-    [(CYNavigationController *)[tabbar_ selectedViewController] reloadData];
+    if (sections_ != nil)
+        [tabs addObject:sections_];
+    if (changes_ != nil)
+        [tabs addObject:changes_];
+    if (manage_ != nil)
+        [tabs addObject:manage_];
+    if (search_ != nil)
+        [tabs addObject:search_];
+    if (sources_ != nil)
+        [tabs addObject:sources_];
+    if (installed_ != nil)
+        [tabs addObject:installed_];
+
+    for (CYNavigationController *tab in tabs)
+        [tab reloadData];
 
     [queueDelegate_ queueStatusDidChange];
     [[[self queueBadgeController] tabBarItem] setBadgeValue:(Queuing_ ? UCLocalize("Q_D") : nil)];
@@ -8288,10 +8445,6 @@ static _finline void _setHomePage(Cydia *self) {
         _error->Discard();
 }
 
-- (CGRect) popUpBounds {
-    return [[tabbar_ view] bounds];
-}
-
 - (bool) perform {
     if (![database_ prepare])
         return false;
@@ -8406,9 +8559,9 @@ static _finline void _setHomePage(Cydia *self) {
     return browser;
 }
 
-- (SectionsController *) sectionsController {
+- (CYSectionsController *) sectionsController {
     if (sections_ == nil)
-        sections_ = [[SectionsController alloc] initWithDatabase:database_];
+        sections_ = [[CYSectionsController alloc] initWithDatabase:database_];
     return sections_;
 }
 
@@ -8436,9 +8589,9 @@ static _finline void _setHomePage(Cydia *self) {
     return search_;
 }
 
-- (SourceTable *) sourcesController {
+- (SourceController *) sourcesController {
     if (sources_ == nil)
-        sources_ = [[SourceTable alloc] initWithDatabase:database_];
+        sources_ = [[SourceController alloc] initWithDatabase:database_];
     return sources_;
 }
 
@@ -8575,7 +8728,9 @@ static _finline void _setHomePage(Cydia *self) {
 }
 
 - (void) system:(NSString *)command { _pooled
+    _trace();
     system([command UTF8String]);
+    _trace();
 }
 
 - (void) applicationWillSuspend {
@@ -8664,12 +8819,12 @@ static _finline void _setHomePage(Cydia *self) {
     if (![path hasPrefix:@"/"])
         path = [@"/" stringByAppendingString:path];
 
-    if ([path isEqualToString:@"/add-source"])
-        return [[[AddSourceController alloc] initWithDatabase:database_] autorelease];
-    else if ([path isEqualToString:@"/storage"])
+    if ([path isEqualToString:@"/storage"])
         return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[CYBrowserController class]];
+    /*else if ([path isEqualToString:@"/add-source"])
+        return [[[AddSourceController alloc] initWithDatabase:database_] autorelease];*/
     else if ([path isEqualToString:@"/sources"])
-        return [[[SourceTable alloc] initWithDatabase:database_] autorelease];
+        return [[[SourceController alloc] initWithDatabase:database_] autorelease];
     else if ([path isEqualToString:@"/packages"])
         return [[[InstalledController alloc] initWithDatabase:database_] autorelease];
     else if ([path hasPrefix:@"/url/"])
@@ -8699,8 +8854,6 @@ static _finline void _setHomePage(Cydia *self) {
     CYViewController *page = nil;
     int tag = 0;
 
-    NSLog(@"open url: %@", url);
-
     if ((page = [self pageForURL:url hasTag:&tag])) {
         [self setPage:page];
         tag_ = tag;
@@ -8712,7 +8865,7 @@ static _finline void _setHomePage(Cydia *self) {
 
 - (void) applicationOpenURL:(NSURL *)url {
     [super applicationOpenURL:url];
-    NSLog(@"first: %@", url);
+
     if (!loaded_) starturl_ = [url retain];
     else [self openCydiaURL:url];
 }
@@ -8787,6 +8940,20 @@ static _finline void _setHomePage(Cydia *self) {
     [tabbar_ setViewControllers:controllers];
 }
 
+- (void)showFakeTabBarInView:(UIView *)view {
+    static UITabBar *fake = [[UITabBar alloc] initWithFrame:CGRectMake(0, 0, 0, 49.0f)];
+    if (view != nil) {
+        CGRect frame = [fake frame];
+        frame.origin.y = [view frame].size.height - frame.size.height;
+        frame.size.width = [view frame].size.width;
+        [fake setFrame:frame];
+        [fake setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin];
+        [view addSubview:fake];
+    } else {
+        [fake removeFromSuperview];
+    }
+}
+
 - (void) applicationDidFinishLaunching:(id)unused {
 _trace();
     CydiaApp = self;
@@ -8812,9 +8979,7 @@ _trace();
     essential_ = [[NSMutableArray alloc] initWithCapacity:4];
     broken_ = [[NSMutableArray alloc] initWithCapacity:4];
 
-    UIScreen *screen([UIScreen mainScreen]);
-
-    window_ = [[UIWindow alloc] initWithFrame:[screen bounds]];
+    window_ = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
     [window_ orderFront:self];
     [window_ makeKey:self];
     [window_ setHidden:NO];
@@ -8847,6 +9012,7 @@ _trace();
 
     // Show pinstripes while loading data.
     [[tabbar_ view] setBackgroundColor:[UIColor pinStripeColor]];
+    [self showFakeTabBarInView:[tabbar_ tabBar]];
 
     [self performSelector:@selector(loadData) withObject:nil afterDelay:0];
 _trace();
@@ -8859,47 +9025,14 @@ _trace();
         return;
     }
 
+    CGRect fixframe = [[tabbar_ view] frame];
+    if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]))
+        fixframe.size = CGSizeMake(fixframe.size.height, fixframe.size.width);
+    CYLoadingIndicator *loading = [[[CYLoadingIndicator alloc] initWithFrame:fixframe] autorelease];
+    [loading setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+    [[tabbar_ view] addSubview:loading];
     [window_ setUserInteractionEnabled:NO];
 
-    UIView *container = [[[UIView alloc] init] autorelease];
-    [container setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin];
-
-    UIActivityIndicatorView *spinner = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
-    [spinner startAnimating];
-    [container addSubview:spinner];
-
-    UILabel *label = [[[UILabel alloc] init] autorelease];
-    [label setFont:[UIFont boldSystemFontOfSize:15.0f]];
-    [label setBackgroundColor:[UIColor clearColor]];
-    [label setTextColor:[UIColor blackColor]];
-    [label setShadowColor:[UIColor whiteColor]];
-    [label setShadowOffset:CGSizeMake(0, 1)];
-    [label setText:[NSString stringWithFormat:Elision_, UCLocalize("LOADING"), nil]];
-    [container addSubview:label];
-
-    CGSize viewsize = [[tabbar_ view] frame].size;
-    CGSize spinnersize = [spinner bounds].size;
-    CGSize textsize = [[label text] sizeWithFont:[label font]];
-    float bothwidth = spinnersize.width + textsize.width + 5.0f;
-
-    CGRect containrect = {
-        CGPointMake(floorf((viewsize.width / 2) - (bothwidth / 2)), floorf((viewsize.height / 2) - (spinnersize.height / 2))),
-        CGSizeMake(bothwidth, spinnersize.height)
-    };
-    CGRect textrect = {
-        CGPointMake(spinnersize.width + 5.0f, floorf((spinnersize.height / 2) - (textsize.height / 2))),
-        textsize
-    };
-    CGRect spinrect = {
-        CGPointZero,
-        spinnersize
-    };
-
-    [container setFrame:containrect];
-    [spinner setFrame:spinrect];
-    [label setFrame:textrect];
-    [[tabbar_ view] addSubview:container];
-
     [self reloadData];
     PrintTimes();
 
@@ -8909,6 +9042,8 @@ _trace();
         _setHomePage(self);
     }
 
+    [self showFakeTabBarInView:nil];
+
     [starturl_ release];
     starturl_ = nil;
 
@@ -8916,7 +9051,7 @@ _trace();
 
     // XXX: does this actually slow anything down?
     [[tabbar_ view] setBackgroundColor:[UIColor clearColor]];
-    [container removeFromSuperview];
+    [loading removeFromSuperview];
 }
 
 - (void) showActionSheet:(UIActionSheet *)sheet fromItem:(UIBarButtonItem *)item {