]> git.saurik.com Git - cydia.git/blobdiff - MobileCydia.mm
Add cydia.popViewController(animated).
[cydia.git] / MobileCydia.mm
index 5c5d6af320a59631d9d15d1b9afc11f46051c0b7..ad48ee54f5e82b8d04b8fea3d3417aa50a4afc4f 100644 (file)
@@ -125,8 +125,6 @@ extern "C" {
 #include "SDURLCache/SDURLCache.h"
 
 #include "substrate.h"
-
-#include "Version.h"
 /* }}} */
 
 /* Profiler {{{ */
@@ -208,6 +206,35 @@ void PrintTimes() {
     while (false); \
     [_pool release];
 
+#define Cydia_ CYDIA_VERSION
+
+#define lprintf(args...) fprintf(stderr, args)
+
+#define ForRelease 1
+#define TraceLogging (1 && !ForRelease)
+#define HistogramInsertionSort (!ForRelease ? 0 : 0)
+#define ProfileTimes (0 && !ForRelease)
+#define ForSaurik (0 && !ForRelease)
+#define LogBrowser (0 && !ForRelease)
+#define TrackResize (0 && !ForRelease)
+#define ManualRefresh (1 && !ForRelease)
+#define ShowInternals (0 && !ForRelease)
+#define AlwaysReload (0 && !ForRelease)
+#define TryIndexedCollation (0 && !ForRelease)
+
+#if !TraceLogging
+#undef _trace
+#define _trace(args...)
+#endif
+
+#if !ProfileTimes
+#undef _profile
+#define _profile(name) {
+#undef _end
+#define _end }
+#define PrintTimes() do {} while (false)
+#endif
+
 // Hash Functions/Structures {{{
 extern "C" uint32_t hashlittle(const void *key, size_t length, uint32_t initval = 0);
 
@@ -293,7 +320,9 @@ static _finline void UpdateExternalStatus(uint64_t newStatus) {
     NSDate *future([NSDate distantFuture]);
     NSString *mode([loop currentMode] ?: NSDefaultRunLoopMode);
 
+_trace();
     while (!stopped && [loop runMode:mode beforeDate:future]);
+_trace();
 
     return [context count] == 0 ? nil : [context objectAtIndex:0];
 }
@@ -360,33 +389,6 @@ static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSC
 static const NSStringCompareOptions LaxCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSCaseInsensitiveSearch;
 static const CFStringCompareFlags LaxCompareFlags_ = kCFCompareCaseInsensitive | kCFCompareNonliteral | kCFCompareLocalized | kCFCompareNumerically | kCFCompareWidthInsensitive | kCFCompareForcedOrdering;
 
-#define lprintf(args...) fprintf(stderr, args)
-
-#define ForRelease 1
-#define TraceLogging (1 && !ForRelease)
-#define HistogramInsertionSort (!ForRelease ? 0 : 0)
-#define ProfileTimes (0 && !ForRelease)
-#define ForSaurik (0 && !ForRelease)
-#define LogBrowser (0 && !ForRelease)
-#define TrackResize (0 && !ForRelease)
-#define ManualRefresh (1 && !ForRelease)
-#define ShowInternals (0 && !ForRelease)
-#define AlwaysReload (0 && !ForRelease)
-#define TryIndexedCollation (0 && !ForRelease)
-
-#if !TraceLogging
-#undef _trace
-#define _trace(args...)
-#endif
-
-#if !ProfileTimes
-#undef _profile
-#define _profile(name) {
-#undef _end
-#define _end }
-#define PrintTimes() do {} while (false)
-#endif
-
 /* Radix Sort {{{ */
 typedef uint32_t (*SKRadixFunction)(id, void *);
 
@@ -842,9 +844,22 @@ class Pcre {
     const char *data_;
 
   public:
+    Pcre() :
+        code_(NULL),
+        study_(NULL)
+    {
+    }
+
     Pcre(const char *regex) :
+        code_(NULL),
         study_(NULL)
     {
+        this->operator =(regex);
+    }
+
+    void operator =(const char *regex) {
+        _assert(code_ == NULL);
+
         const char *error;
         int offset;
         code_ = pcre_compile(regex, 0, &error, &offset, NULL);
@@ -876,6 +891,10 @@ class Pcre {
         data_ = data;
         return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
     }
+
+    _finline size_t size() const {
+        return capture_;
+    }
 };
 /* }}} */
 /* Mime Addresses {{{ */
@@ -1069,10 +1088,10 @@ static NSString *Idiom_;
 
 static NSMutableSet *CydiaHosts_;
 
-static NSString *kCydiaProgressEventTypeError = @"ERROR";
-static NSString *kCydiaProgressEventTypeInformation = @"INFORMATION";
-static NSString *kCydiaProgressEventTypeStatus = @"STATUS";
-static NSString *kCydiaProgressEventTypeWarning = @"WARNING";
+static NSString *kCydiaProgressEventTypeError = @"Error";
+static NSString *kCydiaProgressEventTypeInformation = @"Information";
+static NSString *kCydiaProgressEventTypeStatus = @"Status";
+static NSString *kCydiaProgressEventTypeWarning = @"Warning";
 /* }}} */
 
 /* Display Helpers {{{ */
@@ -4038,7 +4057,50 @@ static NSString *Warning_;
 @end
 /* }}} */
 
-/* Web Scripting {{{ */
+@interface Diversion : NSObject {
+    Pcre pattern_;
+    _H<NSString> key_;
+    _H<NSString> format_;
+}
+
+@end
+
+@implementation Diversion
+
+- (id) initWithFrom:(NSString *)from to:(NSString *)to {
+    if ((self = [super init]) != nil) {
+        pattern_ = [from UTF8String];
+        key_ = from;
+        format_ = to;
+    } return self;
+}
+
+- (NSString *) divert:(NSString *)url {
+    if (!pattern_(url))
+        return nil;
+
+    size_t count(pattern_.size());
+    id values[count];
+    for (size_t i(0); i != count; ++i)
+        values[i] = pattern_[i + 1];
+
+    return [[[NSString alloc] initWithFormat:format_ arguments:reinterpret_cast<va_list>(values)] autorelease];
+}
+
+- (NSString *) key {
+    return key_;
+}
+
+- (NSUInteger) hash {
+    return [key_ hash];
+}
+
+- (BOOL) isEqual:(Diversion *)object {
+    return self == object || [self class] == [object class] && [key_ isEqual:[object key]];
+}
+
+@end
+
 @interface CydiaObject : NSObject {
     id indirect_;
     _transient id delegate_;
@@ -4048,6 +4110,17 @@ static NSString *Warning_;
 
 @end
 
+@interface CYBrowserController : BrowserController {
+    CydiaObject *cydia_;
+}
+
++ (void) addDiversion:(Diversion *)diversion;
+
+@end
+
+static NSMutableSet *Diversions_;
+
+/* Web Scripting {{{ */
 @implementation CydiaObject
 
 - (void) dealloc {
@@ -4141,6 +4214,8 @@ static NSString *Warning_;
         return @"addTrivialSource";
     else if (selector == @selector(close))
         return @"close";
+    else if (selector == @selector(divert::))
+        return @"divert";
     else if (selector == @selector(du:))
         return @"du";
     else if (selector == @selector(stringWithFormat:arguments:))
@@ -4159,6 +4234,8 @@ static NSString *Warning_;
         return @"installPackages";
     else if (selector == @selector(localizedStringForKey:value:table:))
         return @"localize";
+    else if (selector == @selector(popViewController:))
+        return @"popViewController";
     else if (selector == @selector(refreshSources))
         return @"refreshSources";
     else if (selector == @selector(removeButton))
@@ -4203,6 +4280,10 @@ static NSString *Warning_;
     return [feature isEqualToString:@"window.open"];
 }
 
+- (void) divert:(NSString *)from :(NSString *)to {
+    [CYBrowserController performSelectorOnMainThread:@selector(addDiversion:) withObject:[[[Diversion alloc] initWithFrom:from to:to] autorelease] waitUntilDone:NO];
+}
+
 - (NSNumber *) getKernelNumber:(NSString *)name {
     const char *string([name UTF8String]);
 
@@ -4241,6 +4322,12 @@ static NSString *Warning_;
     [delegate_ performSelectorOnMainThread:@selector(addCydiaHost:) withObject:host waitUntilDone:NO];
 }
 
+- (void) popViewController:(NSNumber *)value {
+    if (value == (id) [WebUndefined undefined])
+        value = [NSNumber numberWithBool:YES];
+    [indirect_ performSelectorOnMainThread:@selector(popViewControllerWithNumber:) withObject:value waitUntilDone:NO];
+}
+
 - (void) addTrivialSource:(NSString *)href {
     [delegate_ performSelectorOnMainThread:@selector(addTrivialSource:) withObject:href waitUntilDone:NO];
 }
@@ -4514,16 +4601,12 @@ static NSString *Warning_;
     } return self;
 }
 
-// XXX: factor this out somewhere
-- (UIColor *) groupTableViewBackgroundColor {
-    UIDevice *device([UIDevice currentDevice]);
-    bool iPad([device respondsToSelector:@selector(userInterfaceIdiom)] && [device userInterfaceIdiom] == UIUserInterfaceIdiomPad);
-    return iPad ? [UIColor colorWithRed:0.821 green:0.834 blue:0.860 alpha:1] : [UIColor groupTableViewBackgroundColor];
-}
-
 - (void) loadView {
     [self setView:[[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]];
-    [[self view] setBackgroundColor:[self groupTableViewBackgroundColor]];
+
+    UITableView *table([[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease]);
+    [table setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+    [[self view] addSubview:table];
 
     indicator_ = [[[CYLoadingIndicator alloc] initWithFrame:[[self view] bounds]] autorelease];
     [indicator_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
@@ -4550,12 +4633,6 @@ static NSString *Warning_;
 /* }}} */
 
 /* Cydia Browser Controller {{{ */
-@interface CYBrowserController : BrowserController {
-    CydiaObject *cydia_;
-}
-
-@end
-
 @implementation CYBrowserController
 
 - (void) dealloc {
@@ -4567,6 +4644,14 @@ static NSString *Warning_;
     return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://url/%@", [[[webview_ request] URL] absoluteString]]];
 }
 
++ (void) initialize {
+    Diversions_ = [[NSMutableSet alloc] initWithCapacity:0];
+}
+
++ (void) addDiversion:(Diversion *)diversion {
+    [Diversions_ addObject:diversion];
+}
+
 - (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
     [super webView:view didClearWindowObject:window forFrame:frame];
 
@@ -4582,6 +4667,16 @@ static NSString *Warning_;
 - (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
     NSMutableURLRequest *copy([[super webView:view resource:resource willSendRequest:request redirectResponse:response fromDataSource:source] mutableCopy]);
 
+  divert:
+    NSURL *url([copy URL]);
+    NSString *href([url absoluteString]);
+
+    for (Diversion *diversion in Diversions_)
+        if (NSString *diverted = [diversion divert:href]) {
+            [copy setURL:[NSURL URLWithString:diverted]];
+            goto divert;
+        }
+
     if (System_ != NULL)
         [copy setValue:System_ forHTTPHeaderField:@"X-System"];
     if (Machine_ != NULL)
@@ -7192,7 +7287,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     NSMutableArray *sections_;
     NSMutableArray *filtered_;
     UITableView *list_;
-    BOOL editing_;
 }
 
 - (id) initWithDatabase:(Database *)database;
@@ -7215,24 +7309,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) updateNavigationItem {
-    [[self navigationItem] setTitle:editing_ ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS")];
+    [[self navigationItem] setTitle:[self isEditing] ? UCLocalize("SECTION_VISIBILITY") : UCLocalize("SECTIONS")];
     if ([sections_ count] == 0) {
         [[self navigationItem] setRightBarButtonItem:nil];
     } else {
         [[self navigationItem] setRightBarButtonItem:[[UIBarButtonItem alloc]
-            initWithBarButtonSystemItem:(editing_ ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit)
+            initWithBarButtonSystemItem:([self isEditing] ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit)
             target:self
             action:@selector(editButtonClicked)
         ] animated:([[self navigationItem] rightBarButtonItem] != nil)];
     }
 }
 
-- (BOOL) isEditing {
-    return editing_;
-}
+- (void) setEditing:(BOOL)editing animated:(BOOL)animated {
+    [super setEditing:editing animated:animated];
 
-- (void) setEditing:(BOOL)editing {
-    if ((editing_ = editing))
+    if (editing)
         [list_ reloadData];
     else
         [delegate_ updateData];
@@ -7247,16 +7339,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 
 - (void) viewWillDisappear:(BOOL)animated {
     [super viewWillDisappear:animated];
-    if (editing_) [self setEditing:NO];
+    if ([self isEditing]) [self setEditing:NO];
 }
 
 - (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath {
-    Section *section = (editing_ ? [sections_ objectAtIndex:[indexPath row]] : ([indexPath row] == 0 ? nil : [filtered_ objectAtIndex:([indexPath row] - 1)]));
+    Section *section = nil;
+    int index = [indexPath row];
+    if (![self isEditing]) {
+        index -= 1; 
+        if (index >= 0)
+            section = [filtered_ objectAtIndex:index];
+    } else {
+        section = [sections_ objectAtIndex:index];
+    }
     return section;
 }
 
 - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
-    return editing_ ? [sections_ count] : [filtered_ count] + 1;
+    if ([self isEditing])
+        return [sections_ count];
+    else
+        return [filtered_ count] + 1;
 }
 
 /*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
@@ -7270,13 +7373,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
     if (cell == nil)
         cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
 
-    [cell setSection:[self sectionAtIndexPath:indexPath] editing:editing_];
+    [cell setSection:[self sectionAtIndexPath:indexPath] editing:[self isEditing]];
 
     return cell;
 }
 
 - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
-    if (editing_)
+    if ([self isEditing])
         return;
 
     Section *section = [self sectionAtIndexPath:indexPath];
@@ -7377,7 +7480,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) {
 }
 
 - (void) editButtonClicked {
-    [self setEditing:(!editing_)];
+    [self setEditing:![self isEditing] animated:YES];
 }
 
 @end
@@ -9726,6 +9829,21 @@ _trace();
 _trace();
 }
 
+- (NSArray *) defaultStartPages {
+    NSMutableArray *standard = [NSMutableArray array];
+    [standard addObject:[NSArray arrayWithObject:@"cydia://home"]];
+    [standard addObject:[NSArray arrayWithObject:@"cydia://sections"]];
+    [standard addObject:[NSArray arrayWithObject:@"cydia://changes"]];
+    if (!IsWildcat_) {
+        [standard addObject:[NSArray arrayWithObject:@"cydia://manage"]];
+    } else {
+        [standard addObject:[NSArray arrayWithObject:@"cydia://installed"]];
+        [standard addObject:[NSArray arrayWithObject:@"cydia://sources"]];
+    }
+    [standard addObject:[NSArray arrayWithObject:@"cydia://search"]];
+    return standard;
+}
+
 - (void) loadData {
 _trace();
     if (Role_ == nil) {
@@ -9743,42 +9861,48 @@ _trace();
 
     [self disemulate];
 
-    int selectedIndex = 0;
-    NSMutableArray *items = nil;
+    int savedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue];
+    NSArray *saved = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy];
+    int standardIndex = 0;
+    NSArray *standard = [self defaultStartPages];
 
-    bool recently = false;
-    NSDate *closed([Metadata_ objectForKey:@"LastClosed"]);
-    if (closed != nil) {
+    BOOL valid = YES;
+
+    if (saved == nil)
+        valid = NO;
+
+    NSDate *closed = [Metadata_ objectForKey:@"LastClosed"];
+    if (valid && closed != nil) {
         NSTimeInterval interval([closed timeIntervalSinceNow]);
         // XXX: Is 15 minutes the optimal time here?
-        if (interval <= 0 && interval > -(15*60))
-            recently = true;
+        if (interval > 0 && interval <= -(15*60))
+            valid = NO;
     }
 
-    items = [[Metadata_ objectForKey:@"InterfaceState"] mutableCopy];
-    selectedIndex = [[Metadata_ objectForKey:@"InterfaceIndex"] intValue];
+    if (valid && [saved count] != [standard count])
+        valid = NO;
 
-    BOOL enough = YES;
-    for (NSArray *entry in items)
-        if ([entry count] <= 0)
-            enough = NO;
-
-    if (!recently || !items || !enough) {
-        selectedIndex = 0;
-        items = [NSMutableArray array];
-        [items addObject:[NSArray arrayWithObject:@"cydia://home"]];
-        [items addObject:[NSArray arrayWithObject:@"cydia://sections"]];
-        [items addObject:[NSArray arrayWithObject:@"cydia://changes"]];
-        if (!IsWildcat_) {
-            [items addObject:[NSArray arrayWithObject:@"cydia://manage"]];
-        } else {
-            [items addObject:[NSArray arrayWithObject:@"cydia://installed"]];
-            [items addObject:[NSArray arrayWithObject:@"cydia://sources"]];
+    if (valid) {
+        for (unsigned int i = 0; i < [standard count]; i++) {
+            NSArray *std = [standard objectAtIndex:i], *sav = [saved objectAtIndex:i];
+            // XXX: The "hasPrefix" sanity check here could be, in theory, fooled,
+            //      but it's good enough for now.
+            if ([sav count] == 0 || ![[sav objectAtIndex:0] hasPrefix:[std objectAtIndex:0]]) {
+                valid = NO;
+                break;
+            }
         }
-        [items addObject:[NSArray arrayWithObject:@"cydia://search"]];
     }
 
-    [tabbar_ setSelectedIndex:selectedIndex];
+    NSArray *items = nil;
+    if (valid) {
+        [tabbar_ setSelectedIndex:savedIndex];
+        items = saved;
+    } else {
+        [tabbar_ setSelectedIndex:standardIndex];
+        items = standard;
+    }
+
     for (unsigned int tab = 0; tab < [[tabbar_ viewControllers] count]; tab++) {
         NSArray *stack = [items objectAtIndex:tab];
         CYNavigationController *navigation = [[tabbar_ viewControllers] objectAtIndex:tab];
@@ -9962,11 +10086,14 @@ int main(int argc, char *argv[]) { _pooled
     /* Set Locale {{{ */
     Locale_ = CFLocaleCopyCurrent();
     Languages_ = [NSLocale preferredLanguages];
+
     //CFStringRef locale(CFLocaleGetIdentifier(Locale_));
     //NSLog(@"%@", [Languages_ description]);
 
     const char *lang;
-    if (Languages_ == nil || [Languages_ count] == 0)
+    if (Locale_ != NULL)
+        lang = [(NSString *) CFLocaleGetIdentifier(Locale_) UTF8String];
+    else if (Languages_ == nil || [Languages_ count] == 0)
         // XXX: consider just setting to C and then falling through?
         lang = NULL;
     else {