X-Git-Url: https://git.saurik.com/cydia.git/blobdiff_plain/56e1090818a4e0f5af9e79ce67c2cba5a0842f95..9b96fe8ebf4577688a0b4179edcc71384e15e505:/Cydia.mm diff --git a/Cydia.mm b/Cydia.mm index fda68d20..eecc2be4 100644 --- a/Cydia.mm +++ b/Cydia.mm @@ -35,7 +35,13 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// XXX: wtf/FastMalloc.h... wtf? +#define USE_SYSTEM_MALLOC 1 + /* #include Directives {{{ */ +#import "UICaboodle.h" + +#include #include #include @@ -50,15 +56,7 @@ // XXX: remove #import -#import -//#include - -#include -#include -#include - -#import - +#include #include #include @@ -83,6 +81,8 @@ #include #include #include +#include +#include #include #include @@ -100,12 +100,87 @@ extern "C" { #import "BrowserView.h" #import "ResetView.h" -#import "UICaboodle.h" + +#import "substrate.h" /* }}} */ //#define _finline __attribute__((force_inline)) #define _finline inline +struct timeval _ltv; +bool _itv; + +#define _limit(count) do { \ + static size_t _count(0); \ + if (++_count == count) \ + exit(0); \ +} while (false) + +#define _timestamp ({ \ + struct timeval tv; \ + gettimeofday(&tv, NULL); \ + tv.tv_sec * 1000000 + tv.tv_usec; \ +}) + +typedef std::vector TimeList; +TimeList times_; + +class ProfileTime { + private: + const char *name_; + uint64_t total_; + uint64_t count_; + + public: + ProfileTime(const char *name) : + name_(name), + total_(0) + { + times_.push_back(this); + } + + void AddTime(uint64_t time) { + total_ += time; + ++count_; + } + + void Print() { + if (total_ != 0) + std::cerr << std::setw(5) << count_ << ", " << std::setw(7) << total_ << " : " << name_ << std::endl; + total_ = 0; + count_ = 0; + } +}; + +class ProfileTimer { + private: + ProfileTime &time_; + uint64_t start_; + + public: + ProfileTimer(ProfileTime &time) : + time_(time), + start_(_timestamp) + { + } + + ~ProfileTimer() { + time_.AddTime(_timestamp - start_); + } +}; + +void PrintTimes() { + for (TimeList::const_iterator i(times_.begin()); i != times_.end(); ++i) + (*i)->Print(); + std::cerr << "========" << std::endl; +} + +#define _profile(name) { \ + static ProfileTime name(#name); \ + ProfileTimer _ ## name(name); + +#define _end } + /* Objective-C Handle<> {{{ */ template class _H { @@ -148,11 +223,83 @@ class _H { #define _pooled _H _pool([[NSAutoreleasePool alloc] init], true); +void NSLogPoint(const char *fix, const CGPoint &point) { + NSLog(@"%s(%g,%g)", fix, point.x, point.y); +} + void NSLogRect(const char *fix, const CGRect &rect) { NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); } -static const NSStringCompareOptions CompareOptions_ = NSCaseInsensitiveSearch | NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch | NSForcedOrderingSearch; +@interface NSObject (Cydia) +- (id) yieldToSelector:(SEL)selector withObject:(id)object; +- (id) yieldToSelector:(SEL)selector; +@end + +@implementation NSObject (Cydia) + +- (void) doNothing { +} + +- (void) _yieldToContext:(NSMutableArray *)context { _pooled + SEL selector(reinterpret_cast([[context objectAtIndex:0] pointerValue])); + id object([[context objectAtIndex:1] nonretainedObjectValue]); + volatile bool &stopped(*reinterpret_cast([[context objectAtIndex:2] pointerValue])); + + /* XXX: deal with exceptions */ + id value([self performSelector:selector withObject:object]); + + [context removeAllObjects]; + if (value != nil) + [context addObject:value]; + + stopped = true; + + [self + performSelectorOnMainThread:@selector(doNothing) + withObject:nil + waitUntilDone:NO + ]; +} + +- (id) yieldToSelector:(SEL)selector withObject:(id)object { + /*return [self performSelector:selector withObject:object];*/ + + volatile bool stopped(false); + + NSMutableArray *context([NSMutableArray arrayWithObjects: + [NSValue valueWithPointer:selector], + [NSValue valueWithNonretainedObject:object], + [NSValue valueWithPointer:const_cast(&stopped)], + nil]); + + NSThread *thread([[[NSThread alloc] + initWithTarget:self + selector:@selector(_yieldToContext:) + object:context + ] autorelease]); + + [thread start]; + + NSRunLoop *loop([NSRunLoop currentRunLoop]); + NSDate *future([NSDate distantFuture]); + + while (!stopped && [loop runMode:NSDefaultRunLoopMode beforeDate:future]); + + return [context count] == 0 ? nil : [context objectAtIndex:0]; +} + +- (id) yieldToSelector:(SEL)selector { + return [self yieldToSelector:selector withObject:nil]; +} + +@end + +/* NSForcedOrderingSearch doesn't work on the iPhone */ +static const NSStringCompareOptions MatchCompareOptions_ = NSLiteralSearch | NSCaseInsensitiveSearch; +static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch; +static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_; +static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch; /* iPhoneOS 2.0 Compatibility {{{ */ #ifdef __OBJC2__ @@ -206,18 +353,13 @@ extern NSString * const kCAFilterNearest; @implementation PopTransitionView -- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to { +- (void) transitionViewDidComplete:(UITransitionView *)view fromView:(UIView *)from toView:(UIView *)to { if (from != nil && to == nil) [self removeFromSuperview]; } @end -@interface UIView (PopUpView) -- (void) popFromSuperviewAnimated:(BOOL)animated; -- (void) popSubview:(UIView *)view; -@end - @implementation UIView (PopUpView) - (void) popFromSuperviewAnimated:(BOOL)animated { @@ -239,8 +381,131 @@ extern NSString * const kCAFilterNearest; #define lprintf(args...) fprintf(stderr, args) -#define ForSaurik 1 +#define ForRelease 1 +#define ForSaurik (0 && !ForRelease) +#define LogBrowser (1 && !ForRelease) +#define ManualRefresh (1 && !ForRelease) +#define ShowInternals (1 && !ForRelease) +#define IgnoreInstall (0 && !ForRelease) #define RecycleWebViews 0 +#define AlwaysReload (0 && !ForRelease) + +#if ForRelease +#undef _trace +#define _trace(args...) +#undef _profile +#define _profile(name) { +#undef _end +#define _end } +#define PrintTimes() do {} while (false) +#endif + +/* Radix Sort {{{ */ +@interface NSMutableArray (Radix) +- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object; +@end + +@implementation NSMutableArray (Radix) + +- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object { + NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]); + [invocation setSelector:selector]; + [invocation setArgument:&object atIndex:2]; + + size_t count([self count]); + + struct RadixItem { + size_t index; + uint32_t key; + } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count); + + for (size_t i(0); i != count; ++i) { + RadixItem &item(lhs[i]); + item.index = i; + + id object([self objectAtIndex:i]); + [invocation setTarget:object]; + + [invocation invoke]; + [invocation getReturnValue:&item.key]; + } + + static const size_t width = 32; + static const size_t bits = 11; + static const size_t slots = 1 << bits; + static const size_t passes = (width + (bits - 1)) / bits; + + size_t *hist(new size_t[slots]); + + for (size_t pass(0); pass != passes; ++pass) { + memset(hist, 0, sizeof(size_t) * slots); + + for (size_t i(0); i != count; ++i) { + uint32_t key(lhs[i].key); + key >>= pass * bits; + key &= _not(uint32_t) >> width - bits; + ++hist[key]; + } + + size_t offset(0); + for (size_t i(0); i != slots; ++i) { + size_t local(offset); + offset += hist[i]; + hist[i] = local; + } + + for (size_t i(0); i != count; ++i) { + uint32_t key(lhs[i].key); + key >>= pass * bits; + key &= _not(uint32_t) >> width - bits; + rhs[hist[key]++] = lhs[i]; + } + + RadixItem *tmp(lhs); + lhs = rhs; + rhs = tmp; + } + + delete [] hist; + + NSMutableArray *values([NSMutableArray arrayWithCapacity:count]); + for (size_t i(0); i != count; ++i) + [values addObject:[self objectAtIndex:lhs[i].index]]; + [self setArray:values]; + + delete [] swap; +} + +@end +/* }}} */ + +/* Apple Bug Fixes {{{ */ +@implementation UIWebDocumentView (Cydia) + +- (void) _setScrollerOffset:(CGPoint)offset { + UIScroller *scroller([self _scroller]); + + CGSize size([scroller contentSize]); + CGSize bounds([scroller bounds].size); + + CGPoint max; + max.x = size.width - bounds.width; + max.y = size.height - bounds.height; + + // wtf Apple?! + if (max.x < 0) + max.x = 0; + if (max.y < 0) + max.y = 0; + + offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x; + offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y; + + [scroller setOffset:offset]; +} + +@end +/* }}} */ typedef enum { kUIControlEventMouseDown = 1 << 0, @@ -251,23 +516,40 @@ typedef enum { kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside) } UIControlEventMasks; +NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) { + size_t length([self length] - state->state); + if (length <= 0) + return 0; + else if (length > count) + length = count; + for (size_t i(0); i != length; ++i) + objects[i] = [self item:state->state++]; + state->itemsPtr = objects; + state->mutationsPtr = (unsigned long *) self; + return length; +} + @interface NSString (UIKit) - (NSString *) stringByAddingPercentEscapes; - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1; @end @interface NSString (Cydia) ++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length; + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length; - (NSComparisonResult) compareByPath:(NSString *)other; +- (NSString *) stringByCachingURLWithCurrentCDN; +- (NSString *) stringByAddingPercentEscapesIncludingReserved; @end @implementation NSString (Cydia) ++ (NSString *) stringWithUTF8BytesNoCopy:(const char *)bytes length:(int)length { + return [[[NSString alloc] initWithBytesNoCopy:const_cast(bytes) length:length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; +} + + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length { - char data[length + 1]; - memcpy(data, bytes, length); - data[length] = '\0'; - return [NSString stringWithUTF8String:data]; + return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease]; } - (NSComparisonResult) compareByPath:(NSString *)other { @@ -300,6 +582,26 @@ typedef enum { return result == NSOrderedSame ? value : result; } +- (NSString *) stringByCachingURLWithCurrentCDN { + return [self + stringByReplacingOccurrencesOfString:@"://" + withString:@"://ne.edgecastcdn.net/8003A4/" + options:0 + /* XXX: this is somewhat inaccurate */ + range:NSMakeRange(0, 10) + ]; +} + +- (NSString *) stringByAddingPercentEscapesIncludingReserved { + return [(id)CFURLCreateStringByAddingPercentEscapes( + kCFAllocatorDefault, + (CFStringRef) self, + NULL, + CFSTR(";/?:@&=+$,"), + kCFStringEncodingUTF8 + ) autorelease]; +} + @end /* Perl-Compatible RegEx {{{ */ @@ -357,6 +659,8 @@ class Pcre { - (NSString *) name; - (NSString *) address; +- (void) setAddress:(NSString *)address; + + (Address *) addressWithString:(NSString *)string; - (Address *) initWithString:(NSString *)string; @end @@ -378,6 +682,15 @@ class Pcre { return address_; } +- (void) setAddress:(NSString *)address { + if (address_ != nil) + [address_ autorelease]; + if (address == nil) + address_ = nil; + else + address_ = [address retain]; +} + + (Address *) addressWithString:(NSString *)string { return [[[Address alloc] initWithString:string] autorelease]; } @@ -460,6 +773,9 @@ static const float KeyboardTime_ = 0.3f; #define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist" #define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb" +#define NotifyConfig_ "/etc/notify.conf" + +static bool Queuing_; static CGColor Blue_; static CGColor Blueish_; @@ -467,15 +783,19 @@ static CGColor Black_; static CGColor Off_; static CGColor White_; static CGColor Gray_; +static CGColor Green_; +static CGColor Purple_; +static CGColor Purplish_; + +static UIColor *InstallingColor_; +static UIColor *RemovingColor_; static NSString *App_; static NSString *Home_; static BOOL Sounds_Keyboard_; static BOOL Advanced_; -#if !ForSaurik static BOOL Loaded_; -#endif static BOOL Ignored_; static UIFont *Font12_; @@ -485,20 +805,14 @@ static UIFont *Font18Bold_; static UIFont *Font22Bold_; static const char *Machine_ = NULL; -static const NSString *UniqueID_ = NULL; - -unsigned Major_; -unsigned Minor_; -unsigned BugFix_; +static const NSString *UniqueID_ = nil; +static const NSString *Build_ = nil; +static const NSString *Product_ = nil; +static const NSString *Safari_ = nil; CFLocaleRef Locale_; CGColorSpaceRef space_; -#define FW_LEAST(major, minor, bugfix) \ - (major < Major_ || major == Major_ && \ - (minor < Minor_ || minor == Minor_ && \ - bugfix <= BugFix_)) - bool bootstrap_; bool reload_; @@ -617,6 +931,7 @@ bool isSectionVisible(NSString *section) { @end @protocol CydiaDelegate +- (void) clearPackage:(Package *)package; - (void) installPackage:(Package *)package; - (void) removePackage:(Package *)package; - (void) slideUp:(UIActionSheet *)alert; @@ -625,9 +940,11 @@ bool isSectionVisible(NSString *section) { - (void) syncData; - (void) askForSettings; - (UIProgressHUD *) addProgressHUD; +- (void) removeProgressHUD:(UIProgressHUD *)hud; - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag; - (RVPage *) pageForPackage:(NSString *)name; - (void) openMailToURL:(NSURL *)url; +- (void) clearFirstResponder; @end /* }}} */ @@ -656,6 +973,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()]]; } @@ -715,8 +1033,8 @@ class Progress : protected: virtual void Update() { - [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]]; - [delegate_ setProgressPercent:(Percent / 100)]; + /*[delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]]; + [delegate_ setProgressPercent:(Percent / 100)];*/ } public: @@ -730,13 +1048,15 @@ class Progress : } virtual void Done() { - [delegate_ setProgressPercent:1]; + //[delegate_ setProgressPercent:1]; } }; /* }}} */ /* Database Interface {{{ */ @interface Database : NSObject { + unsigned era_; + pkgCacheFile cache_; pkgDepCache::Policy *policy_; pkgRecords *records_; @@ -759,6 +1079,7 @@ class Progress : } + (Database *) sharedInstance; +- (unsigned) era; - (void) _readCydia:(NSNumber *)fd; - (void) _readStatus:(NSNumber *)fd; @@ -773,6 +1094,7 @@ class Progress : - (pkgRecords *) records; - (pkgProblemResolver *) resolver; - (pkgAcquire &) fetcher; +- (pkgSourceList &) list; - (NSArray *) packages; - (NSArray *) sources; - (void) reloadData; @@ -795,6 +1117,7 @@ class Progress : NSString *description_; NSString *label_; NSString *origin_; + NSString *support_; NSString *uri_; NSString *distribution_; @@ -811,6 +1134,8 @@ class Progress : - (NSComparisonResult) compareByNameAndType:(Source *)source; +- (NSString *) supportForPackage:(NSString *)package; + - (NSDictionary *) record; - (BOOL) trusted; @@ -832,24 +1157,27 @@ class Progress : @implementation Source -- (void) dealloc { - [uri_ release]; - [distribution_ release]; - [type_ release]; +#define _clear(field) \ + if (field != nil) \ + [field release]; \ + field = nil; - if (description_ != nil) - [description_ release]; - if (label_ != nil) - [label_ release]; - if (origin_ != nil) - [origin_ release]; - if (version_ != nil) - [version_ release]; - if (defaultIcon_ != nil) - [defaultIcon_ release]; - if (record_ != nil) - [record_ release]; +- (void) _clear { + _clear(uri_) + _clear(distribution_) + _clear(type_) + + _clear(description_) + _clear(label_) + _clear(origin_) + _clear(support_) + _clear(version_) + _clear(defaultIcon_) + _clear(record_) +} +- (void) dealloc { + [self _clear]; [super dealloc]; } @@ -865,44 +1193,52 @@ class Progress : return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name]; } -- (Source *) initWithMetaIndex:(metaIndex *)index { - if ((self = [super init]) != nil) { - 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]; - - debReleaseIndex *dindex(dynamic_cast(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; +- (void) setMetaIndex:(metaIndex *)index { + [self _clear]; - 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 == "Version") - version_ = [[NSString stringWithUTF8String:value.c_str()] retain]; - } + 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]; + + debReleaseIndex *dindex(dynamic_cast(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]; } + } + + record_ = [Sources_ objectForKey:[self key]]; + if (record_ != nil) + record_ = [record_ retain]; +} - record_ = [Sources_ objectForKey:[self key]]; - if (record_ != nil) - record_ = [record_ retain]; +- (Source *) initWithMetaIndex:(metaIndex *)index { + if ((self = [super init]) != nil) { + [self setMetaIndex:index]; } return self; } @@ -926,7 +1262,11 @@ class Progress : return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; +} + +- (NSString *) supportForPackage:(NSString *)package { + return support_ == nil ? nil : [support_ stringByReplacingOccurrencesOfString:@"*" withString:package]; } - (NSDictionary *) record { @@ -1019,36 +1359,9 @@ class Progress : @end /* }}} */ /* Package Class {{{ */ -NSString *Scour(const char *field, const char *begin, const char *end) { - size_t i(0), l(strlen(field)); - - for (;;) { - const char *name = begin + i; - const char *colon = name + l; - const char *value = colon + 1; - - if ( - value < end && - *colon == ':' && - strncasecmp(name, field, l) == 0 - ) { - while (value != end && value[0] == ' ') - ++value; - const char *line = std::find(value, end, '\n'); - while (line != value && line[-1] == ' ') - --line; - - return [NSString stringWithUTF8Bytes:value length:(line - value)]; - } else { - begin = std::find(begin, end, '\n'); - if (begin == end) - return nil; - ++begin; - } - } -} - @interface Package : NSObject { + unsigned era_; + pkgCache::PkgIterator iterator_; _transient Database *database_; pkgCache::VerIterator version_; @@ -1057,6 +1370,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) { Source *source_; bool cached_; + NSString *section_; + bool essential_; + NSString *latest_; NSString *installed_; @@ -1068,6 +1384,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { NSString *homepage_; Address *sponsor_; Address *author_; + NSString *support_; NSArray *tags_; NSString *role_; @@ -1080,12 +1397,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (pkgCache::PkgIterator) iterator; - (NSString *) section; +- (NSString *) simpleSection; + +- (NSString *) uri; + - (Address *) maintainer; - (size_t) size; - (NSString *) description; -- (NSString *) index; +- (unichar) index; +- (NSMutableDictionary *) metadata; - (NSDate *) seen; +- (BOOL) subscribed; +- (BOOL) ignored; - (NSString *) latest; - (NSString *) installed; @@ -1111,6 +1435,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (NSString *) depiction; - (Address *) author; +- (NSString *) support; + - (NSArray *) files; - (NSArray *) relationships; - (NSArray *) warnings; @@ -1125,19 +1451,20 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (BOOL) hasTag:(NSString *)tag; - (NSString *) primaryPurpose; - (NSArray *) purposes; +- (bool) isCommercial; - (NSComparisonResult) compareByName:(Package *)package; - (NSComparisonResult) compareBySection:(Package *)package; -- (NSComparisonResult) compareBySectionAndName:(Package *)package; -- (NSComparisonResult) compareForChanges:(Package *)package; + +- (uint32_t) compareForChanges; - (void) install; - (void) remove; -- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search; -- (NSNumber *) isInstalledAndVisible:(NSNumber *)number; -- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section; -- (NSNumber *) isVisibleInSource:(Source *)source; +- (bool) isUnfilteredAndSearchedForBy:(NSString *)search; +- (bool) isInstalledAndVisible:(NSNumber *)number; +- (bool) isVisiblyUninstalledInSection:(NSString *)section; +- (bool) isVisibleInSource:(Source *)source; @end @@ -1146,6 +1473,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (void) dealloc { if (source_ != nil) [source_ release]; + if (section_ != nil) + [section_ release]; [latest_ release]; if (installed_ != nil) @@ -1165,6 +1494,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { [sponsor_ release]; if (author_ != nil) [author_ release]; + if (support_ != nil) + [support_ release]; if (tags_ != nil) [tags_ release]; if (role_ != nil) @@ -1176,8 +1507,19 @@ NSString *Scour(const char *field, const char *begin, const char *end) { [super dealloc]; } ++ (NSString *) webScriptNameForSelector:(SEL)selector { + if (selector == @selector(hasTag:)) + return @"hasTag"; + else + return nil; +} + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { + return [self webScriptNameForSelector:selector] == nil; +} + + (NSArray *) _attributeKeys { - return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil]; + return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"mode", @"name", @"purposes", @"section", @"size", @"source", @"sponsor", @"support", @"tagline", @"warnings", nil]; } - (NSArray *) attributeKeys { @@ -1190,111 +1532,216 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database { if ((self = [super init]) != nil) { + _profile(Package$initWithIterator) + @synchronized (database) { + era_ = [database era]; + iterator_ = iterator; database_ = database; - version_ = [database_ policy]->GetCandidateVer(iterator_); + _profile(Package$initWithIterator$Control) + _end + + _profile(Package$initWithIterator$Version) + version_ = [database_ policy]->GetCandidateVer(iterator_); + _end + NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()]; - latest_ = [StripVersion(latest) retain]; - pkgCache::VerIterator current = iterator_.CurrentVer(); - NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()]; - installed_ = [StripVersion(installed) retain]; + _profile(Package$initWithIterator$Latest) + latest_ = latest == nil ? nil : [StripVersion(latest) retain]; + _end - if (!version_.end()) - file_ = version_.FileList(); - else { - pkgCache &cache([database_ cache]); - file_ = pkgCache::VerFileIterator(cache, cache.VerFileP); - } + pkgCache::VerIterator current; + NSString *installed; - id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain]; - - if (!file_.end()) { - pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); - - const char *begin, *end; - parser->GetRec(begin, end); - - name_ = Scour("name", begin, end); - if (name_ != nil) - name_ = [name_ retain]; - tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain]; - icon_ = Scour("icon", begin, end); - if (icon_ != nil) - icon_ = [icon_ retain]; - depiction_ = Scour("depiction", begin, end); - if (depiction_ != nil) - depiction_ = [depiction_ retain]; - homepage_ = Scour("homepage", begin, end); - if (homepage_ == nil) - homepage_ = Scour("website", begin, end); - if ([homepage_ isEqualToString:depiction_]) - homepage_ = nil; - if (homepage_ != nil) - homepage_ = [homepage_ retain]; - NSString *sponsor = Scour("sponsor", begin, end); - if (sponsor != nil) - sponsor_ = [[Address addressWithString:sponsor] retain]; - NSString *author = Scour("author", begin, end); - if (author != nil) - author_ = [[Address addressWithString:author] retain]; - NSString *tags = Scour("tag", begin, end); - if (tags != nil) - tags_ = [[tags componentsSeparatedByString:@", "] retain]; - } + _profile(Package$initWithIterator$Current) + current = iterator_.CurrentVer(); + installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()]; + _end - if (tags_ != nil) - for (int i(0), e([tags_ count]); i != e; ++i) { - NSString *tag = [tags_ objectAtIndex:i]; - if ([tag hasPrefix:@"role::"]) { - role_ = [[tag substringFromIndex:6] retain]; - break; - } + _profile(Package$initWithIterator$Installed) + installed_ = [StripVersion(installed) retain]; + _end + + _profile(Package$initWithIterator$File) + if (!version_.end()) + file_ = version_.FileList(); + else { + pkgCache &cache([database_ cache]); + file_ = pkgCache::VerFileIterator(cache, cache.VerFileP); } + _end + + _profile(Package$initWithIterator$Name) + id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain]; + _end + + if (!file_.end()) + _profile(Package$initWithIterator$Parse) + pkgRecords::Parser *parser; + + _profile(Package$initWithIterator$Parse$Lookup) + parser = &[database_ records]->Lookup(file_); + _end + + const char *begin, *end; + parser->GetRec(begin, end); + + NSString *website(nil); + NSString *sponsor(nil); + NSString *author(nil); + NSString *tag(nil); + + struct { + const char *name_; + NSString **value_; + } names[] = { + {"name", &name_}, + {"icon", &icon_}, + {"depiction", &depiction_}, + {"homepage", &homepage_}, + {"website", &website}, + {"support", &support_}, + {"sponsor", &sponsor}, + {"author", &author}, + {"tag", &tag}, + }; + + while (begin != end) + if (*begin == '\n') { + ++begin; + continue; + } else if (isblank(*begin)) next: { + begin = static_cast(memchr(begin + 1, '\n', end - begin - 1)); + if (begin == NULL) + break; + } else if (const char *colon = static_cast(memchr(begin, ':', end - begin))) { + const char *name(begin); + size_t size(colon - begin); + + begin = static_cast(memchr(begin, '\n', end - begin)); + + { + const char *stop(begin == NULL ? end : begin); + while (stop[-1] == '\r') + --stop; + while (++colon != stop && isblank(*colon)); + + for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i) + if (strncasecmp(names[i].name_, name, size) == 0) { + NSString *value; + + _profile(Package$initWithIterator$Parse$Value) + value = [NSString stringWithUTF8Bytes:colon length:(stop - colon)]; + _end + + *names[i].value_ = value; + break; + } + } + + if (begin == NULL) + break; + ++begin; + } else goto next; + + _profile(Package$initWithIterator$Parse$Retain) + if (name_ != nil) + name_ = [name_ retain]; + _profile(Package$initWithIterator$Parse$Tagline) + tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain]; + _end + if (icon_ != nil) + icon_ = [icon_ retain]; + if (depiction_ != nil) + depiction_ = [depiction_ retain]; + if (homepage_ == nil) + homepage_ = website; + if ([homepage_ isEqualToString:depiction_]) + homepage_ = nil; + if (homepage_ != nil) + homepage_ = [homepage_ retain]; + if (sponsor != nil) + sponsor_ = [[Address addressWithString:sponsor] retain]; + if (author != nil) + author_ = [[Address addressWithString:author] retain]; + if (tag != nil) + tags_ = [[tag componentsSeparatedByString:@", "] retain]; + _end + _end + + _profile(Package$initWithIterator$Tags) + if (tags_ != nil) + for (NSString *tag in tags_) + if ([tag hasPrefix:@"role::"]) { + role_ = [[tag substringFromIndex:6] retain]; + break; + } + _end NSString *solid(latest == nil ? installed : latest); bool changed(false); NSString *key([id_ lowercaseString]); - NSMutableDictionary *metadata = [Packages_ objectForKey:key]; - if (metadata == nil) { - metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys: - now_, @"FirstSeen", - nil]; - - if (solid != nil) - [metadata setObject:solid forKey:@"LastVersion"]; - changed = true; - } else { - NSDate *first([metadata objectForKey:@"FirstSeen"]); - NSDate *last([metadata objectForKey:@"LastSeen"]); - NSString *version([metadata objectForKey:@"LastVersion"]); + _profile(Package$initWithIterator$Metadata) + NSMutableDictionary *metadata = [Packages_ objectForKey:key]; + if (metadata == nil) { + metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys: + now_, @"FirstSeen", + nil] mutableCopy]; - if (first == nil) { - first = last == nil ? now_ : last; - [metadata setObject:first forKey:@"FirstSeen"]; + if (solid != nil) + [metadata setObject:solid forKey:@"LastVersion"]; changed = true; + } else { + NSDate *first([metadata objectForKey:@"FirstSeen"]); + NSDate *last([metadata objectForKey:@"LastSeen"]); + NSString *version([metadata objectForKey:@"LastVersion"]); + + if (first == nil) { + first = last == nil ? now_ : last; + [metadata setObject:first forKey:@"FirstSeen"]; + changed = true; + } + + if (solid != nil) + if (version == nil) { + [metadata setObject:solid forKey:@"LastVersion"]; + changed = true; + } else if (![version isEqualToString:solid]) { + [metadata setObject:solid forKey:@"LastVersion"]; + last = now_; + [metadata setObject:last forKey:@"LastSeen"]; + changed = true; + } } - if (solid != nil) - if (version == nil) { - [metadata setObject:solid forKey:@"LastVersion"]; - changed = true; - } else if (![version isEqualToString:solid]) { - [metadata setObject:solid forKey:@"LastVersion"]; - last = now_; - [metadata setObject:last forKey:@"LastSeen"]; - changed = true; + if (changed) { + [Packages_ setObject:metadata forKey:key]; + Changed_ = true; + } + _end + + const char *section(iterator_.Section()); + if (section == NULL) + section_ = nil; + else { + NSString *name([[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_']); + + lookup: + if (NSDictionary *value = [SectionMap_ objectForKey:name]) + if (NSString *rename = [value objectForKey:@"Rename"]) { + name = rename; + goto lookup; } - } - if (changed) { - [Packages_ setObject:metadata forKey:key]; - Changed_ = true; + section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain]; } - } return self; + + essential_ = (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES; + } _end } return self; } + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database { @@ -1309,27 +1756,35 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } - (NSString *) section { - const char *section = iterator_.Section(); - if (section == NULL) - return nil; - - NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_']; + return section_; +} - lookup: - if (NSDictionary *value = [SectionMap_ objectForKey:name]) - if (NSString *rename = [value objectForKey:@"Rename"]) { - name = rename; - goto lookup; - } +- (NSString *) simpleSection { + if (NSString *section = [self section]) + return Simplify(section); + else + return nil; +} - return [name stringByReplacingCharacter:'_' withCharacter:' ']; +- (NSString *) uri { + return nil; +#if 0 + pkgIndexFile *index; + pkgCache::PkgFileIterator file(file_.File()); + if (![database_ list].FindIndex(file, index)) + return nil; + return [NSString stringWithUTF8String:iterator_->Path]; + //return [NSString stringWithUTF8String:file.Site()]; + //return [NSString stringWithUTF8String:index->ArchiveURI(file.FileName()).c_str()]; +#endif } - (Address *) maintainer { if (file_.end()) return nil; pkgRecords::Parser *parser = &[database_ records]->Lookup(file_); - return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]]; + const std::string &maintainer(parser->Maintainer()); + return maintainer.empty() ? nil : [Address addressWithString:[NSString stringWithUTF8String:maintainer.c_str()]]; } - (size_t) size { @@ -1348,7 +1803,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return nil; NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet]; - for (size_t i(1); i != [lines count]; ++i) { + for (size_t i(1), e([lines count]); i != e; ++i) { NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace]; [trimmed addObject:trim]; } @@ -1356,24 +1811,46 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return [trimmed componentsJoinedByString:@"\n"]; } -- (NSString *) index { - NSString *index = [[[self name] substringToIndex:1] uppercaseString]; - return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123"; +- (unichar) index { + _profile(Package$index) + NSString *name([self name]); + if ([name length] == 0) + return '#'; + unichar character([name characterAtIndex:0]); + if (!isalpha(character)) + return '#'; + return toupper(character); + _end +} + +- (NSMutableDictionary *) metadata { + return [Packages_ objectForKey:[id_ lowercaseString]]; } - (NSDate *) seen { - NSDictionary *metadata([Packages_ objectForKey:[id_ lowercaseString]]); - bool subscribed; - if (NSNumber *isSubscribed = [metadata objectForKey:@"IsSubscribed"]) - subscribed = [isSubscribed boolValue]; - else - subscribed = false; - if (subscribed) + NSDictionary *metadata([self metadata]); + if ([self subscribed]) if (NSDate *last = [metadata objectForKey:@"LastSeen"]) return last; return [metadata objectForKey:@"FirstSeen"]; } +- (BOOL) subscribed { + NSDictionary *metadata([self metadata]); + if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"]) + return [subscribed boolValue]; + else + return false; +} + +- (BOOL) ignored { + NSDictionary *metadata([self metadata]); + if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"]) + return [ignored boolValue]; + else + return false; +} + - (NSString *) latest { return latest_; } @@ -1389,16 +1866,16 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (BOOL) upgradableAndEssential:(BOOL)essential { pkgCache::VerIterator current = iterator_.CurrentVer(); + bool value; if (current.end()) - return essential && [self essential]; - else { - pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_); - return !candidate.end() && candidate != current; - } + value = essential && [self essential] && [self visible]; + else + value = !version_.end() && version_ != current;// && (!essential || ![database_ cache][iterator_].Keep()); + return value; } - (BOOL) essential { - return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES; + return essential_; } - (BOOL) broken { @@ -1442,14 +1919,16 @@ NSString *Scour(const char *field, const char *begin, const char *end) { else return @"Remove"; case pkgDepCache::ModeKeep: - if ((state.iFlags & pkgDepCache::AutoKept) != 0) - return nil; + if ((state.iFlags & pkgDepCache::ReInstall) != 0) + return @"Reinstall"; + /*else if ((state.iFlags & pkgDepCache::AutoKept) != 0) + return nil;*/ else return nil; case pkgDepCache::ModeInstall: - if ((state.iFlags & pkgDepCache::ReInstall) != 0) + /*if ((state.iFlags & pkgDepCache::ReInstall) != 0) return @"Reinstall"; - else switch (state.Status) { + else*/ switch (state.Status) { case -1: return @"Downgrade"; case 0: @@ -1479,17 +1958,17 @@ NSString *Scour(const char *field, const char *begin, const char *end) { } - (UIImage *) icon { - NSString *section = [self section]; - if (section != nil) - section = Simplify(section); + NSString *section = [self simpleSection]; UIImage *icon(nil); - if (NSString *icon = icon_) - icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]]; + if (icon_ != nil) + if ([icon_ hasPrefix:@"file:///"]) + icon = [UIImage imageAtPath:[icon_ substringFromIndex:7]]; if (icon == nil) if (section != nil) icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]; - if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon]) - icon = [UIImage imageAtPath:[icon substringFromIndex:6]]; + if (icon == nil) if (source_ != nil) if (NSString *dicon = [source_ defaultIcon]) + if ([dicon hasPrefix:@"file:///"]) + icon = [UIImage imageAtPath:[dicon substringFromIndex:7]]; if (icon == nil) icon = [UIImage applicationImageNamed:@"unknown.png"]; return icon; @@ -1511,6 +1990,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return author_; } +- (NSString *) support { + return support_ != nil ? support_ : [[self source] supportForPackage:id_]; +} + - (NSArray *) files { NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_]; NSMutableArray *files = [NSMutableArray arrayWithCapacity:128]; @@ -1540,6 +2023,8 @@ NSString *Scour(const char *field, const char *begin, const char *end) { [warnings addObject:@"illegal package identifier"]; else for (size_t i(0); i != length; ++i) if ( + /* XXX: technically this is not allowed */ + (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '0' || name[i] > '9') && (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.') @@ -1547,17 +2032,25 @@ NSString *Scour(const char *field, const char *begin, const char *end) { if (strcmp(name, "cydia") != 0) { bool cydia = false; + bool _private = false; bool stash = false; + bool repository = [[self section] isEqualToString:@"Repositories"]; + if (NSArray *files = [self files]) for (NSString *file in files) if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"]) cydia = true; + else if (!_private && [file isEqualToString:@"/private"]) + _private = true; else if (!stash && [file isEqualToString:@"/var/stash"]) stash = true; - if (cydia) + /* XXX: this is not sensitive enough. only some folders are valid. */ + if (cydia && !repository) [warnings addObject:@"files installed into Cydia.app"]; + if (_private) + [warnings addObject:@"files installed with /private/*"]; if (stash) [warnings addObject:@"files installed to /var/stash"]; } @@ -1602,8 +2095,17 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (Source *) source { if (!cached_) { - source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain]; - cached_ = true; + @synchronized (database_) { + if ([database_ era] != era_ || file_.end()) + source_ = nil; + else { + source_ = [database_ getSource:file_.File()]; + if (source_ != nil) + [source_ retain]; + } + + cached_ = true; + } } return source_; @@ -1619,15 +2121,15 @@ NSString *Scour(const char *field, const char *begin, const char *end) { NSRange range; - range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch]; + range = [[self id] rangeOfString:text options:MatchCompareOptions_]; if (range.location != NSNotFound) return YES; - range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch]; + range = [[self name] rangeOfString:text options:MatchCompareOptions_]; if (range.location != NSNotFound) return YES; - range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch]; + range = [[self tagline] rangeOfString:text options:MatchCompareOptions_]; if (range.location != NSNotFound) return YES; @@ -1671,6 +2173,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return [purposes count] == 0 ? nil : purposes; } +- (bool) isCommercial { + return [self hasTag:@"cydia::commercial"]; +} + - (NSComparisonResult) compareByName:(Package *)package { NSString *lhs = [self name]; NSString *rhs = [package name]; @@ -1685,7 +2191,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; } - (NSComparisonResult) compareBySection:(Package *)package { @@ -1697,51 +2203,44 @@ NSString *Scour(const char *field, const char *begin, const char *end) { else if (lhs != NULL && rhs == NULL) return NSOrderedDescending; else if (lhs != NULL && rhs != NULL) { - NSComparisonResult result = [lhs compare:rhs options:CompareOptions_]; - if (result != NSOrderedSame) - return result; + NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]); + return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_]; } return NSOrderedSame; } -- (NSComparisonResult) compareBySectionAndName:(Package *)package { - NSString *lhs = [self section]; - NSString *rhs = [package section]; +- (uint32_t) compareForChanges { + union { + uint32_t key; - if (lhs == NULL && rhs != NULL) - return NSOrderedAscending; - else if (lhs != NULL && rhs == NULL) - return NSOrderedDescending; - else if (lhs != NULL && rhs != NULL) { - NSComparisonResult result = [lhs compare:rhs]; - if (result != NSOrderedSame) - return result; - } - - return [self compareByName:package]; -} + struct { + uint32_t timestamp : 30; + uint32_t ignored : 1; + uint32_t upgradable : 1; + } bits; + } value; -- (NSComparisonResult) compareForChanges:(Package *)package { - BOOL lhs = [self upgradableAndEssential:YES]; - BOOL rhs = [package upgradableAndEssential:YES]; + bool upgradable([self upgradableAndEssential:YES]); + value.bits.upgradable = upgradable ? 1 : 0; - if (lhs != rhs) - return lhs ? NSOrderedAscending : NSOrderedDescending; - else if (!lhs) { - switch ([[self seen] compare:[package seen]]) { - case NSOrderedAscending: - return NSOrderedDescending; - case NSOrderedSame: - break; - case NSOrderedDescending: - return NSOrderedAscending; - default: - _assert(false); - } + if (upgradable) { + value.bits.timestamp = 0; + value.bits.ignored = [self ignored] ? 0 : 1; + value.bits.upgradable = 1; + } else { + value.bits.timestamp = static_cast([[self seen] timeIntervalSince1970]) >> 2; + value.bits.ignored = 0; + value.bits.upgradable = 0; } - return [self compareByName:package]; + return _not(uint32_t) - value.key; +} + +- (void) clear { + pkgProblemResolver *resolver = [database_ resolver]; + resolver->Clear(iterator_); + resolver->Protect(iterator_); } - (void) install { @@ -1763,33 +2262,40 @@ NSString *Scour(const char *field, const char *begin, const char *end) { [database_ cache]->MarkDelete(iterator_, true); } -- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search { - return [NSNumber numberWithBool:( - [self unfiltered] && [self matches:search] - )]; +- (bool) isUnfilteredAndSearchedForBy:(NSString *)search { + _profile(Package$isUnfilteredAndSearchedForBy) + bool value(true); + + _profile(Package$isUnfilteredAndSearchedForBy$Unfiltered) + value &= [self unfiltered]; + _end + + _profile(Package$isUnfilteredAndSearchedForBy$Match) + value &= [self matches:search]; + _end + + return value; + _end } -- (NSNumber *) isInstalledAndVisible:(NSNumber *)number { - return [NSNumber numberWithBool:( - (![number boolValue] || [self visible]) && [self installed] != nil - )]; +- (bool) isInstalledAndVisible:(NSNumber *)number { + return (![number boolValue] || [self visible]) && [self installed] != nil; } -- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name { +- (bool) isVisiblyUninstalledInSection:(NSString *)name { NSString *section = [self section]; - return [NSNumber numberWithBool:( + return [self visible] && [self installed] == nil && ( name == nil || section == nil && [name length] == 0 || [name isEqualToString:section] - ) - )]; + ); } -- (NSNumber *) isVisibleInSource:(Source *)source { - return [NSNumber numberWithBool:([self source] == source && [self visible])]; +- (bool) isVisibleInSource:(Source *)source { + return [self source] == source && [self visible]; } @end @@ -1797,6 +2303,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { /* Section Class {{{ */ @interface Section : NSObject { NSString *name_; + unichar index_; size_t row_; size_t count_; } @@ -1804,7 +2311,9 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (NSComparisonResult) compareByName:(Section *)section; - (Section *) initWithName:(NSString *)name; - (Section *) initWithName:(NSString *)name row:(size_t)row; +- (Section *) initWithIndex:(unichar)index row:(size_t)row; - (NSString *) name; +- (unichar) index; - (size_t) row; - (size_t) count; - (void) addToCount; @@ -1832,7 +2341,7 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return NSOrderedDescending; } - return [lhs compare:rhs options:CompareOptions_]; + return [lhs compare:rhs options:LaxCompareOptions_]; } - (Section *) initWithName:(NSString *)name { @@ -1842,6 +2351,15 @@ NSString *Scour(const char *field, const char *begin, const char *end) { - (Section *) initWithName:(NSString *)name row:(size_t)row { if ((self = [super init]) != nil) { name_ = [name retain]; + index_ = '\0'; + row_ = row; + } return self; +} + +- (Section *) initWithIndex:(unichar)index row:(size_t)row { + if ((self = [super init]) != nil) { + name_ = [(index == '#' ? @"123" : [NSString stringWithCharacters:&index length:1]) retain]; + index_ = index; row_ = row; } return self; } @@ -1850,6 +2368,10 @@ NSString *Scour(const char *field, const char *begin, const char *end) { return name_; } +- (unichar) index { + return index_; +} + - (size_t) row { return row_; } @@ -1878,6 +2400,10 @@ static NSArray *Finishes_; return instance; } +- (unsigned) era { + return era_; +} + - (void) dealloc { _assert(false); [super dealloc]; @@ -1938,9 +2464,9 @@ static NSArray *Finishes_; withObject:[NSArray arrayWithObjects:string, id, nil] waitUntilDone:YES ]; - else if (type == "pmstatus") + else if (type == "pmstatus") { [delegate_ setProgressTitle:string]; - else if (type == "pmconffile") + } else if (type == "pmconffile") [delegate_ setConfigurationData:string]; else _assert(false); } else _assert(false); @@ -2045,6 +2571,10 @@ static NSArray *Finishes_; return *fetcher_; } +- (pkgSourceList &) list { + return *list_; +} + - (NSArray *) packages { return packages_; } @@ -2117,7 +2647,11 @@ static NSArray *Finishes_; return issues; } -- (void) reloadData { +- (void) reloadData { _pooled + @synchronized (self) { + ++era_; + } + _error->Discard(); delete list_; @@ -2136,6 +2670,7 @@ static NSArray *Finishes_; cache_.Close(); + _trace(); if (!cache_.Open(progress_, true)) { std::string error; if (!_error->PopMessage(error)) @@ -2154,6 +2689,7 @@ static NSArray *Finishes_; return; } + _trace(); now_ = [[NSDate date] retain]; @@ -2186,11 +2722,16 @@ static NSArray *Finishes_; } [packages_ removeAllObjects]; + _trace(); for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator) if (Package *package = [Package packageWithIterator:iterator database:self]) [packages_ addObject:package]; - + _trace(); [packages_ sortUsingSelector:@selector(compareByName:)]; + _trace(); + + _config->Set("Acquire::http::Timeout", 15); + _config->Set("Acquire::http::MaxParallel", 4); } - (void) configure { @@ -2266,7 +2807,9 @@ static NSArray *Finishes_; failed = true; [delegate_ performSelectorOnMainThread:@selector(_setProgressError:) - withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil] + withObject:[NSArray arrayWithObjects: + [NSString stringWithUTF8String:error.c_str()], + nil] waitUntilDone:YES ]; } @@ -2496,6 +3039,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @protocol ConfirmationViewDelegate - (void) cancel; - (void) confirm; +- (void) queue; @end @interface ConfirmationView : BrowserView { @@ -2529,9 +3073,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; + NSString *context([sheet context]); - if ([context isEqualToString:@"remove"]) + if ([context isEqualToString:@"remove"]) { switch (button) { case 1: [self cancel]; @@ -2544,10 +3088,13 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - else if ([context isEqualToString:@"unable"]) - [self cancel]; - [sheet dismiss]; + [sheet dismiss]; + } else if ([context isEqualToString:@"unable"]) { + [self cancel]; + [sheet dismiss]; + } else + [super alertSheet:sheet buttonClicked:button]; } - (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { @@ -2573,8 +3120,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { pkgCacheFile &cache([database_ cache]); NSArray *packages = [database_ packages]; - for (size_t i(0), e = [packages count]; i != e; ++i) { - Package *package = [packages objectAtIndex:i]; + for (Package *package in packages) { pkgCache::PkgIterator iterator = [package iterator]; pkgDepCache::StateCache &state(cache[iterator]); @@ -2658,15 +3204,27 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Cancel"; } -- (NSString *) _rightButtonTitle { - return issues_ == nil ? @"Confirm" : nil; +- (id) rightButtonTitle { + return issues_ != nil ? nil : [super rightButtonTitle]; +} + +- (id) _rightButtonTitle { +#if AlwaysReload || IgnoreInstall + return [super _rightButtonTitle]; +#else + return @"Confirm"; +#endif } - (void) _leftButtonClicked { [self cancel]; } +#if !AlwaysReload - (void) _rightButtonClicked { +#if IgnoreInstall + return [super _rightButtonClicked]; +#endif if (essential_ != nil) [essential_ popupAlertAnimated:YES]; else { @@ -2675,6 +3233,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [delegate_ confirm]; } } +#endif @end /* }}} */ @@ -2735,9 +3294,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { id delegate_; BOOL running_; SHA1SumValue springlist_; + SHA1SumValue notifyconf_; SHA1SumValue sandplate_; - size_t received_; - NSTimeInterval last_; } - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to; @@ -2837,6 +3395,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [status_ setCentersHorizontally:YES]; //[status_ setFont:font]; + _trace(); output_ = [[UITextView alloc] initWithFrame:CGRectMake( 10, @@ -2844,6 +3403,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { bounds.size.width - 20, bounds.size.height - navsize.height - 62 - navrect.size.height )]; + _trace(); //[output_ setTextFont:@"Courier New"]; [output_ setTextSize:12]; @@ -2887,8 +3447,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; - if ([context isEqualToString:@"conffile"]) { + NSString *context([sheet context]); + + if ([context isEqualToString:@"error"]) + [sheet dismiss]; + else if ([context isEqualToString:@"conffile"]) { FILE *input = [database_ input]; switch (button) { @@ -2903,9 +3466,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - } - [sheet dismiss]; + [sheet dismiss]; + } } - (void) closeButtonPushed { @@ -2953,6 +3516,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { Finish_ = 4; } + if (Finish_ < 4) { + FileFd file(NotifyConfig_, FileFd::ReadOnly); + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); + if (!(notifyconf_ == sha1.Result())) + Finish_ = 4; + } + if (Finish_ < 3) { FileFd file(SpringBoard_, FileFd::ReadOnly); MMap mmap(file, MMap::ReadOnly); @@ -2995,9 +3567,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"]; if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) { [info autorelease]; - [info setObject:path forKey:@"Path"]; - [info setObject:@"System" forKey:@"ApplicationType"]; - [system addInfoDictionary:info]; + if ([info objectForKey:@"CFBundleIdentifier"] != nil) { + [info setObject:path forKey:@"Path"]; + [info setObject:@"System" forKey:@"ApplicationType"]; + [system addInfoDictionary:info]; + } } } } else goto error; @@ -3033,9 +3607,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [output_ setText:@""]; [progress_ setProgress:0]; - received_ = 0; - last_ = 0;//[NSDate timeIntervalSinceReferenceDate]; - [close_ removeFromSuperview]; [overlay_ addSubview:progress_]; [overlay_ addSubview:status_]; @@ -3051,6 +3622,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { sandplate_ = sha1.Result(); } + { + FileFd file(NotifyConfig_, FileFd::ReadOnly); + MMap mmap(file, MMap::ReadOnly); + SHA1Summation sha1; + sha1.Add(reinterpret_cast(mmap.Data()), mmap.Size()); + notifyconf_ = sha1.Result(); + } + { FileFd file(SpringBoard_, FileFd::ReadOnly); MMap mmap(file, MMap::ReadOnly); @@ -3121,7 +3700,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) startProgress { - last_ = [NSDate timeIntervalSinceReferenceDate]; } - (void) addProgressOutput:(NSString *)output { @@ -3133,15 +3711,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (bool) isCancelling:(size_t)received { - if (last_ != 0) { - NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; - if (received_ != received) { - received_ = received; - last_ = now; - } else if (now - last_ > 30) - return true; - } - return false; } @@ -3173,7 +3742,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) _setProgressTitle:(NSString *)title { - [status_ setText:title]; + NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]); + for (size_t i(0), e([words count]); i != e; ++i) { + NSString *word([words objectAtIndex:i]); + if (Package *package = [database_ packageWithName:word]) + [words replaceObjectAtIndex:i withObject:[package name]]; + } + + [status_ setText:[words componentsJoinedByString:@" "]]; } - (void) _setProgressPercent:(NSNumber *)percent { @@ -3195,12 +3771,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* }}} */ /* Package Cell {{{ */ -@interface PackageCell : UISimpleTableCell { +@interface PackageCell : UITableCell { UIImage *icon_; NSString *name_; NSString *description_; + bool commercial_; NSString *source_; UIImage *badge_; + bool cached_; + Package *package_; #ifdef USE_BADGES UITextLabel *status_; #endif @@ -3240,6 +3819,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [badge_ release]; badge_ = nil; } + + [package_ release]; + package_ = nil; } - (void) dealloc { @@ -3264,14 +3846,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self clearPackage]; Source *source = [package source]; - NSString *section = [package section]; - if (section != nil) - section = Simplify(section); + NSString *section = [package simpleSection]; icon_ = [[package icon] retain]; name_ = [[package name] retain]; description_ = [[package tagline] retain]; + commercial_ = [package isCommercial]; + + package_ = [package retain]; NSString *label = nil; bool trusted = false; @@ -3312,6 +3895,37 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [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; + } + + [super drawRect:rect]; +} + +- (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); + } + + [super drawBackgroundInRect:rect withFade:fade]; } - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected { @@ -3341,17 +3955,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UISetColor(White_); if (!selected) - UISetColor(Black_); + 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]; if (!selected) - UISetColor(Gray_); + UISetColor(commercial_ ? Purplish_ : Gray_); [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2]; [super drawContentInRect:rect selected:selected]; } +- (void) setSelected:(BOOL)selected withFade:(BOOL)fade { + cached_ = false; + [super setSelected:selected withFade:fade]; +} + + (int) heightForPackage:(Package *)package { NSString *tagline([package tagline]); int height = tagline == nil || [tagline length] == 0 ? -17 : 0; @@ -3448,7 +4067,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { count_ = [[NSString stringWithFormat:@"%d", [section count]] retain]; if (editing_) - [switch_ setValue:isSectionVisible(section_) animated:NO]; + [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO]; } } @@ -3614,6 +4233,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _transient Database *database_; Package *package_; NSString *name_; + bool commercial_; NSMutableArray *buttons_; } @@ -3634,7 +4254,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) _clickButtonWithName:(NSString *)name { - if ([name isEqualToString:@"Install"]) + if ([name isEqualToString:@"Clear"]) + [delegate_ clearPackage:package_]; + else if ([name isEqualToString:@"Install"]) [delegate_ installPackage:package_]; else if ([name isEqualToString:@"Reinstall"]) [delegate_ installPackage:package_]; @@ -3646,18 +4268,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - int count = [buttons_ count]; - _assert(count != 0); - _assert(button <= count + 1); + NSString *context([sheet context]); - if (count != button - 1) - [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]]; + if ([context isEqualToString:@"modify"]) { + int count = [buttons_ count]; + _assert(count != 0); + _assert(button <= count + 1); - [sheet dismiss]; + if (count != button - 1) + [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]]; + + [sheet dismiss]; + } else + [super alertSheet:sheet buttonClicked:button]; } - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { - [[frame windowObject] evaluateWebScript:@"document.base.target = '_top'"]; return [super webView:sender didFinishLoadForFrame:frame]; } @@ -3666,11 +4292,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [super webView:sender didClearWindowObject:window forFrame:frame]; } -#if 0 -- (void) _rightButtonClicked { - /*[super _rightButtonClicked]; - return;*/ +- (bool) _allowJavaScriptPanel { + return commercial_; +} +#if !AlwaysReload +- (void) __rightButtonClicked { int count = [buttons_ count]; _assert(count != 0); @@ -3684,15 +4311,22 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [delegate_ slideUp:[[[UIActionSheet alloc] initWithTitle:nil buttons:buttons - defaultButtonIndex:2 + defaultButtonIndex:([buttons count] - 1) delegate:self - context:@"manage" + context:@"modify" ] autorelease]]; } } + +- (void) _rightButtonClicked { + if (commercial_) + [super _rightButtonClicked]; + else + [self __rightButtonClicked]; +} #endif -- (NSString *) _rightButtonTitle { +- (id) _rightButtonTitle { int count = [buttons_ count]; return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0]; } @@ -3724,9 +4358,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (package != nil) { package_ = [package retain]; name_ = [[package id] retain]; + commercial_ = [package isCommercial]; [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]]; + if ([package_ mode] != nil) + [buttons_ addObject:@"Clear"]; if ([package_ source] == nil); else if ([package_ upgradableAndEssential:NO]) [buttons_ addObject:@"Upgrade"]; @@ -3739,6 +4376,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } +- (bool) isLoading { + return commercial_ ? [super isLoading] : false; +} + - (void) reloadData { [self setPackage:[database_ packageWithName:name_]]; [self reloadButtons]; @@ -3750,17 +4391,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @interface PackageTable : RVPage { _transient Database *database_; NSString *title_; - SEL filter_; - id object_; NSMutableArray *packages_; NSMutableArray *sections_; UISectionList *list_; } -- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object; +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title; - (void) setDelegate:(id)delegate; -- (void) setObject:(id)object; - (void) reloadData; - (void) resetCursor; @@ -3777,8 +4415,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [list_ setDataSource:nil]; [title_ release]; - if (object_ != nil) - [object_ release]; [packages_ release]; [sections_ release]; [list_ release]; @@ -3822,18 +4458,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return; Package *package = [packages_ objectAtIndex:row]; + package = [database_ packageWithName:[package id]]; PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease]; - [view setDelegate:delegate_]; [view setPackage:package]; + [view setDelegate:delegate_]; [book_ pushPage:view]; } -- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object { +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title { if ((self = [super initWithBook:book]) != nil) { database_ = database; title_ = [title retain]; - filter_ = filter; - object_ = object == nil ? nil : [object retain]; packages_ = [[NSMutableArray arrayWithCapacity:16] retain]; sections_ = [[NSMutableArray arrayWithCapacity:16] retain]; @@ -3854,7 +4489,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [table setReusesTableCells:YES]; [self addSubview:list_]; - [self reloadData]; [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; @@ -3865,13 +4499,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { delegate_ = delegate; } -- (void) setObject:(id)object { - if (object_ != nil) - [object_ release]; - if (object == nil) - object_ = nil; - else - object_ = [object retain]; +- (bool) hasPackage:(Package *)package { + return true; } - (void) reloadData { @@ -3880,27 +4509,41 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [packages_ removeAllObjects]; [sections_ removeAllObjects]; - for (size_t i(0); i != [packages count]; ++i) { - Package *package([packages objectAtIndex:i]); - if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue]) - [packages_ addObject:package]; - } + _profile(PackageTable$reloadData$Filter) + for (Package *package in packages) + if ([self hasPackage:package]) + [packages_ addObject:package]; + _end Section *section = nil; - for (size_t offset(0); offset != [packages_ count]; ++offset) { - Package *package = [packages_ objectAtIndex:offset]; - NSString *name = [package index]; + _profile(PackageTable$reloadData$Section) + for (size_t offset(0), end([packages_ count]); offset != end; ++offset) { + Package *package; + unichar index; - if (section == nil || ![[section name] isEqualToString:name]) { - section = [[[Section alloc] initWithName:name row:offset] autorelease]; - [sections_ addObject:section]; - } + _profile(PackageTable$reloadData$Section$Package) + package = [packages_ objectAtIndex:offset]; + index = [package index]; + _end - [section addToCount]; - } + if (section == nil || [section index] != index) { + _profile(PackageTable$reloadData$Section$Allocate) + section = [[[Section alloc] initWithIndex:index row:offset] autorelease]; + _end - [list_ reloadData]; + _profile(PackageTable$reloadData$Section$Add) + [sections_ addObject:section]; + _end + } + + [section addToCount]; + } + _end + + _profile(PackageTable$reloadData$List) + [list_ reloadData]; + _end } - (NSString *) title { @@ -3923,6 +4566,58 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [list_ setShouldHideHeaderInShortLists:hide]; } +@end +/* }}} */ +/* Filtered Package Table {{{ */ +@interface FilteredPackageTable : PackageTable { + SEL filter_; + IMP imp_; + id object_; +} + +- (void) setObject:(id)object; + +- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object; + +@end + +@implementation FilteredPackageTable + +- (void) dealloc { + if (object_ != nil) + [object_ release]; + [super dealloc]; +} + +- (void) setObject:(id)object { + if (object_ != nil) + [object_ release]; + if (object == nil) + object_ = nil; + else + object_ = [object retain]; +} + +- (bool) hasPackage:(Package *)package { + _profile(FilteredPackageTable$hasPackage) + return [package valid] && (*reinterpret_cast(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); + imp_ = method_getImplementation(method); + _assert(imp_ != NULL); + + [self reloadData]; + } return self; +} + @end /* }}} */ @@ -4119,7 +4814,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { Source *source = [sources_ objectAtIndex:row]; - PackageTable *packages = [[[PackageTable alloc] + PackageTable *packages = [[[FilteredPackageTable alloc] initWithBook:book_ database:database_ title:[source label] @@ -4147,6 +4842,35 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [delegate_ syncData]; } +- (void) complete { + [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: + @"deb", @"Type", + href_, @"URI", + @"./", @"Distribution", + nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]]; + + [delegate_ syncData]; +} + +- (NSString *) getWarning { + NSString *href(href_); + NSRange colon([href rangeOfString:@"://"]); + if (colon.location != NSNotFound) + href = [href substringFromIndex:(colon.location + 3)]; + href = [href stringByAddingPercentEscapes]; + href = [@"http://cydia.saurik.com/api/repotag/" stringByAppendingString:href]; + href = [href stringByCachingURLWithCurrentCDN]; + + NSURL *url([NSURL URLWithString:href]); + + NSStringEncoding encoding; + NSError *error(nil); + + if (NSString *warning = [NSString stringWithContentsOfURL:url usedEncoding:&encoding error:&error]) + return [warning length] == 0 ? nil : warning; + return nil; +} + - (void) _endConnection:(NSURLConnection *)connection { NSURLConnection **field = NULL; if (connection == trivial_bz2_) @@ -4161,21 +4885,26 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { trivial_bz2_ == nil && trivial_gz_ == nil ) { - [delegate_ setStatusBarShowsProgress:NO]; - - [hud_ show:NO]; - [hud_ removeFromSuperview]; - [hud_ autorelease]; - hud_ = nil; + bool defer(false); if (trivial_) { - [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys: - @"deb", @"Type", - href_, @"URI", - @"./", @"Distribution", - nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]]; - - [delegate_ syncData]; + if (NSString *warning = [self yieldToSelector:@selector(getWarning)]) { + defer = true; + + UIActionSheet *sheet = [[[UIActionSheet alloc] + initWithTitle:@"Source Warning" + buttons:[NSArray arrayWithObjects:@"Add Anyway", @"Cancel", nil] + defaultButtonIndex:0 + delegate:self + context:@"warning" + ] autorelease]; + + [sheet setNumberOfRows:1]; + + [sheet setBodyText:warning]; + [sheet popupAlertAnimated:YES]; + } else + [self complete]; } else if (error_ != nil) { UIActionSheet *sheet = [[[UIActionSheet alloc] initWithTitle:@"Verification Error" @@ -4200,8 +4929,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [sheet popupAlertAnimated:YES]; } - [href_ release]; - href_ = nil; + [delegate_ setStatusBarShowsProgress:NO]; + [delegate_ removeProgressHUD:hud_]; + + [hud_ autorelease]; + hud_ = nil; + + if (!defer) { + [href_ release]; + href_ = nil; + } if (error_ != nil) { [error_ release]; @@ -4241,8 +4978,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; - if ([context isEqualToString:@"source"]) + NSString *context([sheet context]); + + if ([context isEqualToString:@"source"]) { switch (button) { case 1: { NSString *href = [[sheet textField] text]; @@ -4261,7 +4999,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { trivial_ = false; - hud_ = [delegate_ addProgressHUD]; + hud_ = [[delegate_ addProgressHUD] retain]; [hud_ setText:@"Verifying URL"]; } break; @@ -4272,7 +5010,29 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _assert(false); } - [sheet dismiss]; + [sheet dismiss]; + } else if ([context isEqualToString:@"trivial"]) + [sheet dismiss]; + else if ([context isEqualToString:@"urlerror"]) + [sheet dismiss]; + else if ([context isEqualToString:@"warning"]) { + switch (button) { + case 1: + [self complete]; + break; + + case 2: + break; + + default: + _assert(false); + } + + [href_ release]; + href_ = nil; + + [sheet dismiss]; + } } - (id) initWithBook:(RVBook *)book database:(Database *)database { @@ -4311,7 +5071,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [sources_ removeAllObjects]; [sources_ addObjectsFromArray:[database_ sources]]; + _trace(); [sources_ sortUsingSelector:@selector(compareByNameAndType:)]; + _trace(); int count = [sources_ count]; for (offset_ = 0; offset_ != count; ++offset_) { @@ -4341,12 +5103,16 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { context:@"source" ] autorelease]; + [sheet setNumberOfRows:1]; + [sheet addTextFieldWithValue:@"http://" label:@""]; UITextInputTraits *traits = [[sheet textField] textInputTraits]; - [traits setAutocapitalizationType:0]; - [traits setKeyboardType:3]; - [traits setAutocorrectionType:1]; + [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone]; + [traits setAutocorrectionType:UITextAutocorrectionTypeNo]; + [traits setKeyboardType:UIKeyboardTypeURL]; + // XXX: UIReturnKeyDone + [traits setReturnKeyType:UIReturnKeyNext]; [sheet popupAlertAnimated:YES]; } @@ -4366,7 +5132,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit"; } @@ -4380,7 +5146,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { /* Installed View {{{ */ @interface InstalledView : RVPage { _transient Database *database_; - PackageTable *packages_; + FilteredPackageTable *packages_; BOOL expert_; } @@ -4399,7 +5165,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ((self = [super initWithBook:book]) != nil) { database_ = database; - packages_ = [[PackageTable alloc] + packages_ = [[FilteredPackageTable alloc] initWithBook:book database:database title:nil @@ -4437,7 +5203,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Packages"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple"; } @@ -4462,7 +5228,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @implementation HomeView - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - [sheet dismiss]; + NSString *context([sheet context]); + + if ([context isEqualToString:@"about"]) + [sheet dismiss]; + else + [super alertSheet:sheet buttonClicked:button]; } - (void) _leftButtonClicked { @@ -4475,7 +5246,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { ] autorelease]; [sheet setBodyText: - @"Copyright (C) 2008\n" + @"Copyright (C) 2008-2009\n" "Jay Freeman (saurik)\n" "saurik@saurik.com\n" "http://www.saurik.com/\n" @@ -4518,544 +5289,376 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Settings"; } -- (NSString *) _rightButtonTitle { - return nil; -} - -@end -/* }}} */ - -@interface WebView (Cydia) -- (void) setScriptDebugDelegate:(id)delegate; -- (void) _setFormDelegate:(id)delegate; -- (void) _setUIKitDelegate:(id)delegate; -- (void) setWebMailDelegate:(id)delegate; -- (void) _setLayoutInterval:(float)interval; -@end - -/* Indirect Delegate {{{ */ -@interface IndirectDelegate : NSProxy { - _transient volatile id delegate_; -} - -- (void) setDelegate:(id)delegate; -- (id) initWithDelegate:(id)delegate; -@end - -@implementation IndirectDelegate - -- (void) setDelegate:(id)delegate { - delegate_ = delegate; +#if !AlwaysReload +- (id) _rightButtonTitle { + return Queuing_ ? @"Queue" : nil; } -- (id) initWithDelegate:(id)delegate { - delegate_ = delegate; - return self; +- (UINavigationButtonStyle) rightButtonStyle { + return Queuing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal; } -- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel { - if (delegate_ != nil) - if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]) - return sig; - // XXX: I fucking hate Apple so very very bad - return [NSMethodSignature signatureWithObjCTypes:"v@:"]; +- (void) _rightButtonClicked { + [delegate_ queue]; } +#endif -- (void) forwardInvocation:(NSInvocation *)inv { - SEL sel = [inv selector]; - if (delegate_ != nil && [delegate_ respondsToSelector:sel]) - [inv invokeWithTarget:delegate_]; +- (bool) isLoading { + return false; } @end /* }}} */ -/* Browser Implementation {{{ */ -@implementation BrowserView - -- (void) dealloc { - WebView *webview = [webview_ webView]; - [webview setFrameLoadDelegate:nil]; - [webview setResourceLoadDelegate:nil]; - [webview setUIDelegate:nil]; - [webview setScriptDebugDelegate:nil]; - [webview setPolicyDelegate:nil]; - - [webview setDownloadDelegate:nil]; - [webview _setFormDelegate:nil]; - [webview _setUIKitDelegate:nil]; - [webview setWebMailDelegate:nil]; - [webview setEditingDelegate:nil]; +#include - [webview_ setDelegate:nil]; - [webview_ setGestureDelegate:nil]; - - //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - [webview close]; - -#if RecycleWebViews - [webview_ removeFromSuperview]; - [Documents_ addObject:[webview_ autorelease]]; -#else - [webview_ release]; -#endif +/* Cydia Book {{{ */ +@interface CYBook : RVBook < + ProgressDelegate +> { + _transient Database *database_; + UINavigationBar *overlay_; + UINavigationBar *underlay_; + UIProgressIndicator *indicator_; + UITextLabel *prompt_; + UIProgressBar *progress_; + UINavigationButton *cancel_; + bool updating_; +} - [indirect_ setDelegate:nil]; - [indirect_ release]; +- (id) initWithFrame:(CGRect)frame database:(Database *)database; +- (void) update; +- (BOOL) updating; + +@end - [scroller_ setDelegate:nil]; +@implementation CYBook - [scroller_ release]; - [urls_ release]; +- (void) dealloc { + [overlay_ release]; [indicator_ release]; - if (title_ != nil) - [title_ release]; + [prompt_ release]; + [progress_ release]; + [cancel_ release]; [super dealloc]; } -- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy { - [self loadRequest:[NSURLRequest - requestWithURL:url - cachePolicy:policy - timeoutInterval:30.0 - ]]; +- (NSString *) getTitleForPage:(RVPage *)page { + return Simplify([super getTitleForPage:page]); } -- (void) loadURL:(NSURL *)url { - [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy]; +- (BOOL) updating { + return updating_; } -- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request { - NSMutableURLRequest *copy = [request mutableCopy]; - - if (Machine_ != NULL) - [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"]; - if (UniqueID_ != nil) - [copy addValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"]; +- (void) update { + [UIView beginAnimations:nil context:NULL]; - if (Role_ != nil) - [copy addValue:Role_ forHTTPHeaderField:@"X-Role"]; + CGRect ovrframe = [overlay_ frame]; + ovrframe.origin.y = 0; + [overlay_ setFrame:ovrframe]; - return copy; -} + CGRect barframe = [navbar_ frame]; + barframe.origin.y += ovrframe.size.height; + [navbar_ setFrame:barframe]; -- (void) loadRequest:(NSURLRequest *)request { - pushed_ = true; - [webview_ loadRequest:request]; -} + CGRect trnframe = [transition_ frame]; + trnframe.origin.y += ovrframe.size.height; + trnframe.size.height -= ovrframe.size.height; + [transition_ setFrame:trnframe]; -- (void) reloadURL { - if ([urls_ count] == 0) - return; - NSURL *url = [[[urls_ lastObject] retain] autorelease]; - [urls_ removeLastObject]; - [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData]; -} + [UIView endAnimations]; -- (WebView *) webView { - return [webview_ webView]; -} + [indicator_ startAnimation]; + [prompt_ setText:@"Updating Database"]; + [progress_ setProgress:0]; -- (void) view:(UIView *)sender didSetFrame:(CGRect)frame { - [scroller_ setContentSize:frame.size]; -} + updating_ = true; + [overlay_ addSubview:cancel_]; -- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old { - [self view:sender didSetFrame:frame]; + [NSThread + detachNewThreadSelector:@selector(_update) + toTarget:self + withObject:nil + ]; } -- (void) pushPage:(RVPage *)page { - [self setBackButtonTitle:title_]; - [page setDelegate:delegate_]; - [book_ pushPage:page]; -} +- (void) _update_ { + updating_ = false; -- (BOOL) getSpecial:(NSURL *)url { - NSString *href([url absoluteString]); - NSString *scheme([[url scheme] lowercaseString]); + [indicator_ stopAnimation]; - RVPage *page = nil; + [UIView beginAnimations:nil context:NULL]; - if ([href hasPrefix:@"apptapp://package/"]) - page = [delegate_ pageForPackage:[href substringFromIndex:18]]; - else if ([scheme isEqualToString:@"cydia"]) { - page = [delegate_ pageForURL:url hasTag:NULL]; - if (page == nil) - return false; - } else if (![scheme isEqualToString:@"apptapp"]) - return false; + CGRect ovrframe = [overlay_ frame]; + ovrframe.origin.y = -ovrframe.size.height; + [overlay_ setFrame:ovrframe]; - if (page != nil) - [self pushPage:page]; - return true; -} + CGRect barframe = [navbar_ frame]; + barframe.origin.y -= ovrframe.size.height; + [navbar_ setFrame:barframe]; -- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame { - [window setValue:delegate_ forKey:@"cydia"]; -} + CGRect trnframe = [transition_ frame]; + trnframe.origin.y -= ovrframe.size.height; + trnframe.size.height += ovrframe.size.height; + [transition_ setFrame:trnframe]; -- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)dictionary request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id)listener { - if (NSURL *url = [request URL]) { - if (![self getSpecial:url]) { - NSString *scheme([[url scheme] lowercaseString]); - if ([scheme isEqualToString:@"mailto"]) - [delegate_ openMailToURL:url]; - else goto use; - } + [UIView commitAnimations]; - [listener ignore]; - } else use: - [listener use]; + [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0]; } -- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener { - NSURL *url([request URL]); - - if (url == nil) use: { - [listener use]; - return; - } - - const NSArray *capability(reinterpret_cast(GSSystemGetCapability(kGSDisplayIdentifiersCapability))); +- (id) initWithFrame:(CGRect)frame database:(Database *)database { + if ((self = [super initWithFrame:frame]) != nil) { + database_ = database; - if ( - [capability containsObject:@"com.apple.Maps"] && [url mapsURL] || - [capability containsObject:@"com.apple.youtube"] && [url youTubeURL] - ) { - open: - [UIApp openURL:url]; - ignore: - [listener ignore]; - return; - } + CGRect ovrrect = [navbar_ bounds]; + ovrrect.size.height = [UINavigationBar defaultSize].height; + ovrrect.origin.y = -ovrrect.size.height; - int store(_not(int)); - if (NSURL *itms = [url itmsURL:&store]) { - if ( - store == 1 && [capability containsObject:@"com.apple.MobileStore"] || - store == 2 && [capability containsObject:@"com.apple.AppStore"] - ) { - url = itms; - goto open; - } - } + overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; + [self addSubview:overlay_]; - NSString *scheme([[url scheme] lowercaseString]); + 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_]; - if ([scheme isEqualToString:@"tel"]) { - // XXX: intelligence - goto open; - } + [overlay_ setBarStyle:1]; + [underlay_ setBarStyle:1]; - if ([scheme isEqualToString:@"mailto"]) { - [delegate_ openMailToURL:url]; - goto ignore; - } + int barstyle = [overlay_ _barStyle:NO]; + bool ugly = barstyle == 0; - if ([self getSpecial:url]) - goto ignore; - else if ([WebView _canHandleRequest:request]) - goto use; - else if ([url isSpringboardHandledURL]) - goto open; - else - goto use; -} + UIProgressIndicatorStyle style = ugly ? + UIProgressIndicatorStyleMediumBrown : + UIProgressIndicatorStyleMediumWhite; -- (void) webView:(WebView *)sender setStatusText:(NSString *)text { - //lprintf("Status:%s\n", [text UTF8String]); -} + CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style]; + unsigned indoffset = (ovrrect.size.height - indsize.height) / 2; + CGRect indrect = {{indoffset, indoffset}, indsize}; -- (void) _pushPage { - if (pushed_) - return; - pushed_ = true; - [book_ pushPage:self]; -} + indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect]; + [indicator_ setStyle:style]; + [overlay_ addSubview:indicator_]; -- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource { - NSURL *url = [request URL]; - if ([self getSpecial:url]) - return nil; - [self _pushPage]; - return [self _addHeadersToRequest:request]; -} + CGSize prmsize = {215, indsize.height + 4}; -- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed { - [self setBackButtonTitle:title_]; + CGRect prmrect = {{ + indoffset * 2 + indsize.width, +#ifdef __OBJC2__ + -1 + +#endif + unsigned(ovrrect.size.height - prmsize.height) / 2 + }, prmsize}; - BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease]; - [browser setDelegate:delegate_]; + UIFont *font = [UIFont systemFontOfSize:15]; - if (pushed) { - [browser loadRequest:[self _addHeadersToRequest:request]]; - [book_ pushPage:browser]; - } + prompt_ = [[UITextLabel alloc] initWithFrame:prmrect]; - return [browser webView]; -} + [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]]; + [prompt_ setBackgroundColor:[UIColor clearColor]]; + [prompt_ setFont:font]; -- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request { - return [self _createWebViewWithRequest:request pushed:(request != nil)]; -} + [overlay_ addSubview:prompt_]; -- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features { - return [self _createWebViewWithRequest:request pushed:YES]; -} + CGSize prgsize = {75, 100}; -- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; + CGRect prgrect = {{ + ovrrect.size.width - prgsize.width - 10, + (ovrrect.size.height - prgsize.height) / 2 + } , prgsize}; - title_ = [title retain]; - [book_ reloadTitleForPage:self]; -} + progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; + [progress_ setStyle:0]; + [overlay_ addSubview:progress_]; -- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; + cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted]; + [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside]; - reloading_ = false; - loading_ = true; - [indicator_ startAnimation]; - [self reloadButtons]; + CGRect frame = [cancel_ frame]; + frame.size.width = 65; + frame.origin.x = ovrrect.size.width - frame.size.width - 5; + frame.origin.y = (ovrrect.size.height - frame.size.height) / 2; + [cancel_ setFrame:frame]; - if (title_ != nil) { - [title_ release]; - title_ = nil; - } + [cancel_ setBarStyle:barstyle]; + } return self; +} - [book_ reloadTitleForPage:self]; +- (void) _onCancel { + updating_ = false; + [cancel_ removeFromSuperview]; +} - WebView *webview = [webview_ webView]; - NSString *href = [webview mainFrameURL]; - [urls_ addObject:[NSURL URLWithString:href]]; +- (void) _update { _pooled + Status status; + status.setDelegate(self); - [scroller_ scrollPointVisibleAtTopLeft:CGPointZero]; + [database_ updateWithStatus:status]; - CGRect webrect = [scroller_ bounds]; - webrect.size.height = 0; - [webview_ setFrame:webrect]; + [self + performSelectorOnMainThread:@selector(_update_) + withObject:nil + waitUntilDone:NO + ]; } -- (void) _finishLoading { - if (!reloading_) { - loading_ = false; - [indicator_ stopAnimation]; - [self reloadButtons]; - } +- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { + [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]]; } -- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame { - return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame]; +- (void) setProgressTitle:(NSString *)title { + [self + performSelectorOnMainThread:@selector(_setProgressTitle:) + withObject:title + waitUntilDone:YES + ]; } -- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame { - return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame]; +- (void) setProgressPercent:(float)percent { + [self + performSelectorOnMainThread:@selector(_setProgressPercent:) + withObject:[NSNumber numberWithFloat:percent] + waitUntilDone:YES + ]; } -- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame { - return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame]; +- (void) startProgress { } -- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame { - return [webview_ webView:sender didCommitLoadForFrame:frame]; +- (void) addProgressOutput:(NSString *)output { + [self + performSelectorOnMainThread:@selector(_addProgressOutput:) + withObject:output + waitUntilDone:YES + ]; } -- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame { - return [webview_ webView:sender didReceiveDocTypeForFrame:frame]; +- (bool) isCancelling:(size_t)received { + return !updating_; } -- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { - if ([frame parentFrame] == nil) - [self _finishLoading]; - return [webview_ webView:sender didFinishLoadForFrame:frame]; +- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { + [sheet dismiss]; } -- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame { - if ([frame parentFrame] != nil) - return; - [self _finishLoading]; - - [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", - [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString], - [[error localizedDescription] stringByAddingPercentEscapes] - ]]]; +- (void) _setProgressTitle:(NSString *)title { + [prompt_ setText:title]; } -- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary { -#if ForSaurik - lprintf("Console:%s\n", [[dictionary description] UTF8String]); -#endif +- (void) _setProgressPercent:(NSNumber *)percent { + [progress_ setProgress:[percent floatValue]]; } -- (id) initWithBook:(RVBook *)book { - if ((self = [super initWithBook:book]) != nil) { - loading_ = false; - - struct CGRect bounds = [self bounds]; - - UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease]; - [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]]; - [self addSubview:pinstripe]; - - scroller_ = [[UIScroller alloc] initWithFrame:bounds]; - [self addSubview:scroller_]; - - [scroller_ setScrollingEnabled:YES]; - [scroller_ setAdjustForContentSizeChange:YES]; - [scroller_ setClipsSubviews:YES]; - [scroller_ setAllowsRubberBanding:YES]; - [scroller_ setScrollDecelerationFactor:0.99]; - [scroller_ setDelegate:self]; - - CGRect webrect = [scroller_ bounds]; - webrect.size.height = 0; - - WebView *webview; - -#if RecycleWebViews - webview_ = [Documents_ lastObject]; - if (webview_ != nil) { - webview_ = [webview_ retain]; - webview = [webview_ webView]; - [Documents_ removeLastObject]; - [webview_ setFrame:webrect]; - } else { -#else - if (true) { -#endif - webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect]; - webview = [webview_ webView]; - - [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)]; - - [webview_ setAllowsMessaging:YES]; - - [webview_ setTilingEnabled:YES]; - [webview_ setDrawsGrid:NO]; - [webview_ setLogsTilingChanges:NO]; - [webview_ setTileMinificationFilter:kCAFilterNearest]; - [webview_ setDetectsPhoneNumbers:NO]; - [webview_ setAutoresizes:YES]; - - [webview_ setViewportSize:CGSizeMake(980, -1) forDocumentTypes:0x10]; - [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x2]; - [webview_ setViewportSize:CGSizeMake(320, -1) forDocumentTypes:0x8]; - - [webview_ _setDocumentType:0x4]; - - [webview_ setZoomsFocusedFormControl:YES]; - [webview_ setContentsPosition:7]; - [webview_ setEnabledGestures:0xa]; - [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x4]; - [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:0x7]; - - [webview_ setSmoothsFonts:YES]; - - [webview _setUsesLoaderCache:YES]; - [webview setGroupName:@"Cydia"]; - //[webview _setLayoutInterval:0.5]; - } - - [webview_ setDelegate:self]; - [webview_ setGestureDelegate:self]; - [scroller_ addSubview:webview_]; - - //NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - - CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite]; - indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)]; - [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite]; - - Package *package([[Database sharedInstance] packageWithName:@"cydia"]); - NSString *application = package == nil ? @"Cydia" : [NSString - stringWithFormat:@"Cydia/%@", - [package installed] - ]; [webview setApplicationNameForUserAgent:application]; +- (void) _addProgressOutput:(NSString *)output { +} - indirect_ = [[IndirectDelegate alloc] initWithDelegate:self]; +@end +/* }}} */ +/* Cydia:// Protocol {{{ */ +@interface CydiaURLProtocol : NSURLProtocol { +} - [webview setFrameLoadDelegate:self]; - [webview setResourceLoadDelegate:indirect_]; - [webview setUIDelegate:self]; - [webview setScriptDebugDelegate:self]; - [webview setPolicyDelegate:self]; +@end - urls_ = [[NSMutableArray alloc] initWithCapacity:16]; +@implementation CydiaURLProtocol - [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - [pinstripe setAutoresizingMask:UIViewAutoresizingFlexibleHeight]; - } return self; ++ (BOOL) canInitWithRequest:(NSURLRequest *)request { + NSURL *url([request URL]); + if (url == nil) + return NO; + NSString *scheme([[url scheme] lowercaseString]); + if (scheme == nil || ![scheme isEqualToString:@"cydia"]) + return NO; + return YES; } -- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event { - [webview_ redrawScaledDocument]; ++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { + return request; } -- (void) _rightButtonClicked { - reloading_ = true; - [self reloadURL]; -} +- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request { + id client([self client]); + if (icon == nil) + [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]]; + else { + NSData *data(UIImagePNGRepresentation(icon)); -- (NSString *) _rightButtonTitle { - return @"Reload"; + NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]); + [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; + [client URLProtocol:self didLoadData:data]; + [client URLProtocolDidFinishLoading:self]; + } } -- (NSString *) rightButtonTitle { - return loading_ ? @"" : [self _rightButtonTitle]; -} +- (void) startLoading { + id client([self client]); + NSURLRequest *request([self request]); -- (NSString *) title { - return title_ == nil ? @"Loading" : title_; -} + NSURL *url([request URL]); + NSString *href([url absoluteString]); -- (NSString *) backButtonTitle { - return @"Browser"; -} + NSString *path([href substringFromIndex:8]); + NSRange slash([path rangeOfString:@"/"]); -- (void) setPageActive:(BOOL)active { - if (!active) - [indicator_ removeFromSuperview]; - else - [[book_ navigationBar] addSubview:indicator_]; -} + NSString *command; + if (slash.location == NSNotFound) { + command = path; + path = nil; + } else { + command = [path substringToIndex:slash.location]; + path = [path substringFromIndex:(slash.location + 1)]; + } -- (void) resetViewAnimated:(BOOL)animated { -} + Database *database([Database sharedInstance]); -- (void) setPushed:(bool)pushed { - pushed_ = pushed; + if ([command isEqualToString:@"package-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + Package *package([database packageWithName:path]); + if (package == nil) + goto fail; + UIImage *icon([package icon]); + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"source-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *source(Simplify(path)); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]); + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"uikit-image"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + UIImage *icon(_UIImageWithName(path)); + [self _returnPNGWithImage:icon forRequest:request]; + } else if ([command isEqualToString:@"section-icon"]) { + if (path == nil) + goto fail; + path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *section(Simplify(path)); + UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]); + if (icon == nil) + icon = [UIImage applicationImageNamed:@"unknown.png"]; + [self _returnPNGWithImage:icon forRequest:request]; + } else fail: { + [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]]; + } } -@end -/* }}} */ - -@interface CYBook : RVBook < - ProgressDelegate -> { - _transient Database *database_; - UINavigationBar *overlay_; - UINavigationBar *underlay_; - UIProgressIndicator *indicator_; - UITextLabel *prompt_; - UIProgressBar *progress_; - UINavigationButton *cancel_; - bool updating_; - size_t received_; - NSTimeInterval last_; +- (void) stopLoading { } -- (id) initWithFrame:(CGRect)frame database:(Database *)database; -- (void) update; -- (BOOL) updating; - @end +/* }}} */ -/* Install View {{{ */ -@interface InstallView : RVPage { +/* Sections View {{{ */ +@interface SectionsView : RVPage { _transient Database *database_; NSMutableArray *sections_; NSMutableArray *filtered_; @@ -5071,7 +5674,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end -@implementation InstallView +@implementation SectionsView - (void) dealloc { [list_ setDataSource:nil]; @@ -5136,7 +5739,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } } - PackageTable *table = [[[PackageTable alloc] + PackageTable *table = [[[FilteredPackageTable alloc] initWithBook:book_ database:database_ title:title @@ -5190,8 +5793,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]]; NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32]; - for (size_t i(0); i != [packages count]; ++i) { - Package *package([packages objectAtIndex:i]); + _trace(); + for (Package *package in packages) { NSString *name([package section]); if (name != nil) { @@ -5205,15 +5808,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ([package valid] && [package installed] == nil && [package visible]) [filtered addObject:package]; } + _trace(); [sections_ addObjectsFromArray:[sections allValues]]; [sections_ sortUsingSelector:@selector(compareByName:)]; + _trace(); [filtered sortUsingSelector:@selector(compareBySection:)]; + _trace(); Section *section = nil; - for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) { - Package *package = [filtered objectAtIndex:offset]; + for (Package *package in filtered) { NSString *name = [package section]; if (section == nil || name != nil && ![[section name] isEqualToString:name]) { @@ -5225,8 +5830,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [section addToCount]; } + _trace(); [list_ reloadData]; + _trace(); } - (void) resetView { @@ -5241,10 +5848,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) _rightButtonClicked { if ((editing_ = !editing_)) [list_ reloadData]; - else { + else [delegate_ updateData]; - } - [book_ reloadTitleForPage:self]; [book_ reloadButtonsForPage:self]; } @@ -5257,7 +5862,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return @"Sections"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit"; } @@ -5387,53 +5992,61 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [packages_ removeAllObjects]; [sections_ removeAllObjects]; - for (size_t i(0); i != [packages count]; ++i) { - Package *package([packages objectAtIndex:i]); - + _trace(); + for (Package *package in packages) if ( [package installed] == nil && [package valid] && [package visible] || - [package upgradableAndEssential:NO] + [package upgradableAndEssential:YES] ) [packages_ addObject:package]; - } - [packages_ sortUsingSelector:@selector(compareForChanges:)]; + _trace(); + [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil]; + _trace(); Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease]; + Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease]; Section *section = nil; + NSDate *last = nil; upgrades_ = 0; bool unseens = false; CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle); + _trace(); for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) { Package *package = [packages_ objectAtIndex:offset]; - if ([package upgradableAndEssential:YES]) { - ++upgrades_; - [upgradable addToCount]; - } else { + if (![package upgradableAndEssential:YES]) { unseens = true; NSDate *seen = [package seen]; - NSString *name; + if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) { + last = seen; - if (seen == nil) - name = [@"n/a ?" retain]; - else { - name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen); - } + NSString *name; + if (seen == nil) + name = @"unknown?"; + else { + name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen); + [name autorelease]; + } - if (section == nil || ![[section name] isEqualToString:name]) { + name = [@"New at " stringByAppendingString:name]; section = [[[Section alloc] initWithName:name row:offset] autorelease]; [sections_ addObject:section]; } - [name release]; [section addToCount]; + } else if ([package ignored]) + [ignored addToCount]; + else { + ++upgrades_; + [upgradable addToCount]; } } + _trace(); CFRelease(formatter); @@ -5444,6 +6057,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [sections_ removeLastObject]; } + if ([ignored count] != 0) + [sections_ insertObject:ignored atIndex:0]; if (upgrades_ != 0) [sections_ insertObject:upgradable atIndex:0]; @@ -5459,7 +6074,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return [(CYBook *)book_ updating] ? nil : @"Refresh"; } -- (NSString *) rightButtonTitle { +- (id) rightButtonTitle { return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_]; } @@ -5478,7 +6093,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIView *accessory_; UISearchField *field_; UITransitionView *transition_; - PackageTable *table_; + FilteredPackageTable *table_; UIPreferencesTable *advanced_; UIView *dimmed_; bool flipped_; @@ -5596,10 +6211,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if ((self = [super initWithBook:book]) != nil) { CGRect pageBounds = [book_ pageBounds]; - /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease]; - [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]]; - [self addSubview:pinstripe];*/ - transition_ = [[UITransitionView alloc] initWithFrame:pageBounds]; [self addSubview:transition_]; @@ -5613,7 +6224,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { CGColor dimmed(space_, 0, 0, 0, 0.5); [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]]; - table_ = [[PackageTable alloc] + table_ = [[FilteredPackageTable alloc] initWithBook:book database:database title:nil @@ -5652,10 +6263,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [field_ setPaddingTop:5]; - UITextInputTraits *traits = [field_ textInputTraits]; - [traits setAutocapitalizationType:0]; - [traits setAutocorrectionType:1]; - [traits setReturnKeyType:6]; + 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}}; @@ -5707,7 +6318,10 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { if (flipped_) [self flipPage]; [table_ setObject:[field_ text]]; - [table_ reloadData]; + _profile(SearchView$reloadData) + [table_ reloadData]; + _end + PrintTimes(); [table_ resetCursor]; } @@ -5731,305 +6345,198 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { @end /* }}} */ -@implementation CYBook - -- (void) dealloc { - [overlay_ release]; - [indicator_ release]; - [prompt_ release]; - [progress_ release]; - [cancel_ release]; - [super dealloc]; -} - -- (NSString *) getTitleForPage:(RVPage *)page { - return Simplify([super getTitleForPage:page]); -} - -- (BOOL) updating { - return updating_; -} - -- (void) update { - [UIView beginAnimations:nil context:NULL]; - - CGRect ovrframe = [overlay_ frame]; - ovrframe.origin.y = 0; - [overlay_ setFrame:ovrframe]; - - CGRect barframe = [navbar_ frame]; - barframe.origin.y += ovrframe.size.height; - [navbar_ setFrame:barframe]; - - CGRect trnframe = [transition_ frame]; - trnframe.origin.y += ovrframe.size.height; - trnframe.size.height -= ovrframe.size.height; - [transition_ setFrame:trnframe]; - - [UIView endAnimations]; - - [indicator_ startAnimation]; - [prompt_ setText:@"Updating Database"]; - [progress_ setProgress:0]; - - received_ = 0; - last_ = [NSDate timeIntervalSinceReferenceDate]; - updating_ = true; - [overlay_ addSubview:cancel_]; - - [NSThread - detachNewThreadSelector:@selector(_update) - toTarget:self - withObject:nil - ]; +@interface SettingsView : RVPage { + _transient Database *database_; + NSString *name_; + Package *package_; + UIPreferencesTable *table_; + _UISwitchSlider *subscribedSwitch_; + _UISwitchSlider *ignoredSwitch_; + UIPreferencesControlTableCell *subscribedCell_; + UIPreferencesControlTableCell *ignoredCell_; } -- (void) _update_ { - updating_ = false; - - [indicator_ stopAnimation]; - - [UIView beginAnimations:nil context:NULL]; - - CGRect ovrframe = [overlay_ frame]; - ovrframe.origin.y = -ovrframe.size.height; - [overlay_ setFrame:ovrframe]; +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package; - 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 SettingsView - [UIView commitAnimations]; +- (void) dealloc { + [table_ setDataSource:nil]; - [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0]; + [name_ release]; + if (package_ != nil) + [package_ release]; + [table_ release]; + [subscribedSwitch_ release]; + [ignoredSwitch_ release]; + [subscribedCell_ release]; + [ignoredCell_ release]; + [super dealloc]; } -- (id) initWithFrame:(CGRect)frame database:(Database *)database { - if ((self = [super initWithFrame:frame]) != nil) { - database_ = database; - - CGRect ovrrect = [navbar_ bounds]; - ovrrect.size.height = [UINavigationBar defaultSize].height; - ovrrect.origin.y = -ovrrect.size.height; - - overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; - [self addSubview:overlay_]; - - ovrrect.origin.y = frame.size.height; - underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect]; - [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]]; - [self addSubview:underlay_]; - - [overlay_ setBarStyle:1]; - [underlay_ setBarStyle:1]; - - int barstyle = [overlay_ _barStyle:NO]; - bool ugly = barstyle == 0; - - UIProgressIndicatorStyle style = ugly ? - UIProgressIndicatorStyleMediumBrown : - UIProgressIndicatorStyleMediumWhite; - - CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style]; - unsigned indoffset = (ovrrect.size.height - indsize.height) / 2; - CGRect indrect = {{indoffset, indoffset}, indsize}; - - indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect]; - [indicator_ setStyle:style]; - [overlay_ addSubview:indicator_]; - - CGSize prmsize = {215, indsize.height + 4}; - - CGRect prmrect = {{ - indoffset * 2 + indsize.width, -#ifdef __OBJC2__ - -1 + -#endif - unsigned(ovrrect.size.height - prmsize.height) / 2 - }, prmsize}; - - 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}; +- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table { + if (package_ == nil) + return 0; - CGRect prgrect = {{ - ovrrect.size.width - prgsize.width - 10, - (ovrrect.size.height - prgsize.height) / 2 - } , prgsize}; + return 2; +} - progress_ = [[UIProgressBar alloc] initWithFrame:prgrect]; - [progress_ setStyle:0]; - [overlay_ addSubview:progress_]; +- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group { + if (package_ == nil) + return nil; - cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted]; - [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside]; + switch (group) { + case 0: return nil; + case 1: return nil; - CGRect frame = [cancel_ frame]; - frame.size.width = 65; - frame.origin.x = ovrrect.size.width - frame.size.width - 5; - frame.origin.y = (ovrrect.size.height - frame.size.height) / 2; - [cancel_ setFrame:frame]; + default: _assert(false); + } - [cancel_ setBarStyle:barstyle]; - } return self; + return nil; } -- (void) _onCancel { - updating_ = false; - [cancel_ removeFromSuperview]; -} +- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group { + if (package_ == nil) + return NO; -- (void) _update { _pooled - Status status; - status.setDelegate(self); + switch (group) { + case 0: return NO; + case 1: return YES; - [database_ updateWithStatus:status]; + default: _assert(false); + } - [self - performSelectorOnMainThread:@selector(_update_) - withObject:nil - waitUntilDone:NO - ]; + return NO; } -- (void) setProgressError:(NSString *)error forPackage:(NSString *)id { - [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]]; -} +- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group { + if (package_ == nil) + return 0; -- (void) setProgressTitle:(NSString *)title { - [self - performSelectorOnMainThread:@selector(_setProgressTitle:) - withObject:title - waitUntilDone:YES - ]; -} + switch (group) { + case 0: return 1; + case 1: return 1; -- (void) setProgressPercent:(float)percent { - [self - performSelectorOnMainThread:@selector(_setProgressPercent:) - withObject:[NSNumber numberWithFloat:percent] - waitUntilDone:YES - ]; -} + default: _assert(false); + } -- (void) startProgress { + return 0; } -- (void) addProgressOutput:(NSString *)output { - [self - performSelectorOnMainThread:@selector(_addProgressOutput:) - withObject:output - waitUntilDone:YES - ]; -} +- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key { + if (package_ == nil) + return; -- (bool) isCancelling:(size_t)received { - NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; - if (received_ != received) { - received_ = received; - last_ = now; - } else if (now - last_ > 15) - return true; - return !updating_; -} + _UISwitchSlider *slider([cell control]); + BOOL value([slider value] != 0); + NSMutableDictionary *metadata([package_ metadata]); -- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - [sheet dismiss]; -} + BOOL before; + if (NSNumber *number = [metadata objectForKey:key]) + before = [number boolValue]; + else + before = NO; -- (void) _setProgressTitle:(NSString *)title { - [prompt_ setText:title]; + if (value != before) { + [metadata setObject:[NSNumber numberWithBool:value] forKey:key]; + Changed_ = true; + [delegate_ updateData]; + } } -- (void) _setProgressPercent:(NSNumber *)percent { - [progress_ setProgress:[percent floatValue]]; +- (void) onSubscribed:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsSubscribed"]; } -- (void) _addProgressOutput:(NSString *)output { +- (void) onIgnored:(UIPreferencesControlTableCell *)cell { + [self onSomething:cell withKey:@"IsIgnored"]; } -@end - -@interface CydiaURLProtocol : NSURLProtocol { -} +- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group { + if (package_ == nil) + return nil; -@end + switch (group) { + case 0: switch (row) { + case 0: + return subscribedCell_; + case 1: + return ignoredCell_; + default: _assert(false); + } break; + + case 1: switch (row) { + case 0: { + UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]); + [cell setShowSelection:NO]; + [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."]; + return cell; + } -@implementation CydiaURLProtocol + default: _assert(false); + } break; -+ (BOOL) canInitWithRequest:(NSURLRequest *)request { - NSURL *url([request URL]); - if (url == nil) - return NO; - NSString *scheme([[url scheme] lowercaseString]); - if (scheme == nil || ![scheme isEqualToString:@"cydia"]) - return NO; - return YES; -} + default: _assert(false); + } -+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request { - return request; + return nil; } -- (void) startLoading { - id client([self client]); - NSURLRequest *request([self request]); +- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package { + if ((self = [super initWithBook:book])) { + database_ = database; + name_ = [package retain]; - NSURL *url([request URL]); - NSString *href([url absoluteString]); + table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]]; + [self addSubview:table_]; - NSString *path([href substringFromIndex:8]); - NSRange slash([path rangeOfString:@"/"]); + subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside]; - NSString *command; - if (slash.location == NSNotFound) { - command = path; - path = nil; - } else { - command = [path substringToIndex:slash.location]; - path = [path substringFromIndex:(slash.location + 1)]; - } + ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)]; + [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside]; - Database *database([Database sharedInstance]); + subscribedCell_ = [[UIPreferencesControlTableCell alloc] init]; + [subscribedCell_ setShowSelection:NO]; + [subscribedCell_ setTitle:@"Show All Changes"]; + [subscribedCell_ setControl:subscribedSwitch_]; - if ([command isEqualToString:@"package-icon"]) { - if (path == nil) - goto fail; - Package *package([database packageWithName:path]); - if (package == nil) - goto fail; + ignoredCell_ = [[UIPreferencesControlTableCell alloc] init]; + [ignoredCell_ setShowSelection:NO]; + [ignoredCell_ setTitle:@"Ignore Upgrades"]; + [ignoredCell_ setControl:ignoredSwitch_]; - NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]); + [table_ setDataSource:self]; + [self reloadData]; + } return self; +} - UIImage *icon([package icon]); - NSData *data(UIImagePNGRepresentation(icon)); +- (void) resetViewAnimated:(BOOL)animated { + [table_ resetViewAnimated:animated]; +} - [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; - [client URLProtocol:self didLoadData:data]; - [client URLProtocolDidFinishLoading:self]; - } else fail: { - [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]]; +- (void) reloadData { + if (package_ != nil) + [package_ autorelease]; + package_ = [database_ packageWithName:name_]; + if (package_ != nil) { + [package_ retain]; + [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO]; + [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO]; } + + [table_ reloadData]; } -- (void) stopLoading { +- (NSString *) title { + return @"Settings"; } @end +/* Signature View {{{ */ @interface SignatureView : BrowserView { _transient Database *database_; NSString *package_; @@ -6059,11 +6566,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } return self; } +- (void) resetViewAnimated:(BOOL)animated { +} + - (void) reloadData { [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]]; } @end +/* }}} */ @interface Cydia : UIApplication < ConfirmationViewDelegate, @@ -6091,7 +6602,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { UIKeyboard *keyboard_; UIProgressHUD *hud_; - InstallView *install_; + SectionsView *sections_; ChangesView *changes_; ManageView *manage_; SearchView *search_; @@ -6125,7 +6636,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")] buttons:[NSArray arrayWithObjects: @"Upgrade Essential", - @"Upgrade Everything", + @"Complete Upgrade", @"Ignore (Temporary)", nil] defaultButtonIndex:0 @@ -6139,12 +6650,17 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) _reloadData { - /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_]; - [hud setText:@"Reloading Data"]; - [overlay_ addSubview:hud]; - [hud show:YES];*/ + UIView *block(); + + static bool loaded(false); + UIProgressHUD *hud([self addProgressHUD]); + [hud setText:(loaded ? @"Reloading Data" : @"Loading Data")]; + loaded = true; + + [database_ yieldToSelector:@selector(reloadData) withObject:nil]; + _trace(); - [database_ reloadData]; + [self removeProgressHUD:hud]; size_t changes(0); @@ -6166,7 +6682,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSString *badge([[NSNumber numberWithInt:changes] stringValue]); [buttonbar_ setBadgeValue:badge forButton:3]; if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)]) - [buttonbar_ setBadgeAnimated:YES forButton:3]; + [buttonbar_ setBadgeAnimated:([essential_ count] != 0) forButton:3]; [self setApplicationBadge:badge]; } else { [buttonbar_ setBadgeValue:nil forButton:3]; @@ -6175,27 +6691,33 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self removeApplicationBadge]; } + Queuing_ = false; + [buttonbar_ setBadgeValue:nil forButton:4]; + [self updateData]; -#if !ForSaurik + // XXX: what is this line of code for? if ([packages count] == 0); - else if (Loaded_) -#endif + else if (Loaded_ || ManualRefresh) loaded: [self _loaded]; -#if !ForSaurik else { Loaded_ = YES; + + if (NSDate *update = [Metadata_ objectForKey:@"LastUpdate"]) { + NSTimeInterval interval([update timeIntervalSinceNow]); + if (interval <= 0 && interval > -600) + goto loaded; + } + [book_ update]; } -#endif - - /*[hud show:NO]; - [hud removeFromSuperview];*/ } - (void) _saveConfig { if (Changed_) { + _trace(); _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES); + _trace(); Changed_ = false; } } @@ -6204,11 +6726,11 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self _saveConfig]; /* XXX: this is just stupid */ - if (tag_ != 2) - [install_ reloadData]; - if (tag_ != 3) + if (tag_ != 2 && sections_ != nil) + [sections_ reloadData]; + if (tag_ != 3 && changes_ != nil) [changes_ reloadData]; - if (tag_ != 5) + if (tag_ != 5 && search_ != nil) [search_ reloadData]; [book_ reloadData]; @@ -6224,8 +6746,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { NSArray *keys = [Sources_ allKeys]; - for (int i(0), e([keys count]); i != e; ++i) { - NSString *key = [keys objectAtIndex:i]; + for (NSString *key in keys) { NSDictionary *source = [Sources_ objectForKey:key]; fprintf(file, "%s %s %s\n", @@ -6262,17 +6783,39 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _error->Discard(); } +- (void) popUpBook:(RVBook *)book { + [underlay_ popSubview:book]; +} + +- (CGRect) popUpBounds { + return [underlay_ bounds]; +} + - (void) perform { [database_ prepare]; - confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]]; + confirm_ = [[RVBook alloc] initWithFrame:[self popUpBounds]]; [confirm_ setDelegate:self]; ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]); [page setDelegate:self]; [confirm_ setPage:page]; - [underlay_ popSubview:confirm_]; + [self popUpBook:confirm_]; +} + +- (void) queue { + @synchronized (self) { + [self perform]; + } +} + +- (void) clearPackage:(Package *)package { + @synchronized (self) { + [package clear]; + [self resolve]; + [self perform]; + } } - (void) installPackage:(Package *)package { @@ -6299,8 +6842,19 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) cancel { + [self slideUp:[[[UIActionSheet alloc] + initWithTitle:nil + buttons:[NSArray arrayWithObjects:@"Continue Queuing", @"Cancel and Clear", nil] + defaultButtonIndex:1 + delegate:self + context:@"cancel" + ] autorelease]]; +} + +- (void) complete { @synchronized (self) { [self _reloadData]; + if (confirm_ != nil) { [confirm_ release]; confirm_ = nil; @@ -6342,7 +6896,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [confirm_ popFromSuperviewAnimated:NO]; } - [self cancel]; + [self complete]; } - (void) setPage:(RVPage *)page { @@ -6367,12 +6921,12 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [book_ resetViewAnimated:YES]; return; } else if (tag_ == 2 && tag != 2) - [install_ resetView]; + [sections_ resetView]; switch (tag) { case 1: [self _setHomePage]; break; - case 2: [self setPage:install_]; break; + case 2: [self setPage:sections_]; break; case 3: [self setPage:changes_]; break; case 4: [self setPage:manage_]; break; case 5: [self setPage:search_]; break; @@ -6408,9 +6962,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { - (void) finish { if (hud_ != nil) { [self setStatusBarShowsProgress:NO]; + [self removeProgressHUD:hud_]; - [hud_ show:NO]; - [hud_ removeFromSuperview]; [hud_ autorelease]; hud_ = nil; @@ -6428,6 +6981,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { return; } + _trace(); overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]]; CGRect screenrect = [UIHardware fullScreenApplicationContentRect]; @@ -6520,10 +7074,15 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { CGSize keysize = [UIKeyboard defaultSize]; CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize}; keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect]; - [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)]; + //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)]; [overlay_ addSubview:keyboard_]; - install_ = [[InstallView alloc] initWithBook:book_ database:database_]; + if (!bootstrap_) + [underlay_ addSubview:overlay_]; + + [self reloadData]; + + sections_ = [[SectionsView alloc] initWithBook:book_ database:database_]; changes_ = [[ChangesView alloc] initWithBook:book_ database:database_]; search_ = [[SearchView alloc] initWithBook:book_ database:database_]; @@ -6532,10 +7091,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { withClass:[ManageView class] ] retain]; - if (!bootstrap_) - [underlay_ addSubview:overlay_]; - - [self reloadData]; + PrintTimes(); if (bootstrap_) [self bootstrap]; @@ -6544,13 +7100,47 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button { - NSString *context = [sheet context]; - if ([context isEqualToString:@"fixhalf"]) + NSString *context([sheet context]); + + if ([context isEqualToString:@"missing"]) + [sheet dismiss]; + else if ([context isEqualToString:@"cancel"]) { + bool clear; + + 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:@"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 (int i = 0, e = [broken_ count]; i != e; ++i) { - Package *broken = [broken_ objectAtIndex:i]; + for (Package *broken in broken_) { [broken remove]; NSString *id = [broken id]; @@ -6573,7 +7163,9 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { default: _assert(false); } - else if ([context isEqualToString:@"role"]) { + + [sheet dismiss]; + } else if ([context isEqualToString:@"role"]) { switch (button) { case 1: Role_ = @"User"; break; case 2: Role_ = @"Hacker"; break; @@ -6598,14 +7190,14 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self updateData]; else [self finish]; - } else if ([context isEqualToString:@"upgrade"]) + + [sheet dismiss]; + } else if ([context isEqualToString:@"upgrade"]) { switch (button) { case 1: @synchronized (self) { - for (int i = 0, e = [essential_ count]; i != e; ++i) { - Package *essential = [essential_ objectAtIndex:i]; + for (Package *essential in essential_) [essential install]; - } [self resolve]; [self perform]; @@ -6624,7 +7216,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { _assert(false); } - [sheet dismiss]; + [sheet dismiss]; + } } - (void) reorganize { _pooled @@ -6648,21 +7241,33 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (UIProgressHUD *) addProgressHUD { - UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_]; + UIProgressHUD *hud([[[UIProgressHUD alloc] initWithWindow:window_] autorelease]); + [window_ setUserInteractionEnabled:NO]; [hud show:YES]; - [underlay_ addSubview:hud]; + [progress_ addSubview:hud]; return hud; } +- (void) removeProgressHUD:(UIProgressHUD *)hud { + [hud show:NO]; + [hud removeFromSuperview]; + [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]; + [UIApp openURL:url];// asPanel:YES]; #endif } +- (void) clearFirstResponder { + if (id responder = [window_ firstResponder]) + [responder resignFirstResponder]; +} + - (RVPage *) pageForPackage:(NSString *)name { if (Package *package = [database_ packageWithName:name]) { PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease]; @@ -6687,27 +7292,39 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag { - NSString *href = [url absoluteString]; - if (tag != NULL) tag = 0; - if ([href isEqualToString:@"cydia://add-source"]) + NSString *scheme([[url scheme] lowercaseString]); + if (![scheme isEqualToString:@"cydia"]) + return nil; + NSString *path([url absoluteString]); + if ([path length] < 8) + return nil; + path = [path substringFromIndex:8]; + if (![path hasPrefix:@"/"]) + path = [@"/" stringByAppendingString:path]; + + if ([path isEqualToString:@"/add-source"]) return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease]; - else if ([href isEqualToString:@"cydia://sources"]) + else if ([path isEqualToString:@"/storage"]) + return [self _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"storage" ofType:@"html"]] withClass:[BrowserView class]]; + else if ([path isEqualToString:@"/sources"]) return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease]; - else if ([href isEqualToString:@"cydia://packages"]) + else if ([path isEqualToString:@"/packages"]) return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease]; - else if ([href hasPrefix:@"cydia://url/"]) - return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]]; - else if ([href hasPrefix:@"cydia://launch/"]) - [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO]; - else if ([href hasPrefix:@"cydia://package-signature/"]) - return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease]; - else if ([href hasPrefix:@"cydia://package/"]) - return [self pageForPackage:[href substringFromIndex:16]]; - else if ([href hasPrefix:@"cydia://files/"]) { - NSString *name = [href substringFromIndex:14]; + else if ([path hasPrefix:@"/url/"]) + return [self _pageForURL:[NSURL URLWithString:[path substringFromIndex:5]] withClass:[BrowserView 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]; + else if ([path hasPrefix:@"/package-signature/"]) + return [[[SignatureView alloc] initWithBook:book_ database: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]; @@ -6730,6 +7347,7 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { } - (void) applicationDidFinishLaunching:(id)unused { + _trace(); Font12_ = [[UIFont systemFontOfSize:12] retain]; Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain]; Font14_ = [[UIFont systemFontOfSize:14] retain]; @@ -6774,8 +7392,8 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { ) { [self setIdleTimerDisabled:YES]; - hud_ = [self addProgressHUD]; - [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"]; + hud_ = [[self addProgressHUD] retain]; + [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"]; [self setStatusBarShowsProgress:YES]; @@ -6788,22 +7406,6 @@ bool DepSubstrate(const pkgCache::VerIterator &iterator) { [self finish]; } -/* Web Scripting {{{ */ -+ (NSString *) webScriptNameForSelector:(SEL)selector { - if (selector == @selector(supports:)) - return @"supports"; - return nil; -} - -+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { - return selector != @selector(supports:); -} - -- (BOOL) supports:(NSString *)feature { - return [feature isEqualToString:@"window.open"]; -} -/* }}} */ - - (void) showKeyboard:(BOOL)show { CGSize keysize = [UIKeyboard defaultSize]; CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize}; @@ -6846,8 +7448,7 @@ void AddPreferences(NSString *plist) { _pooled bool cydia(false); - for (size_t i(0); i != [items count]; ++i) { - NSMutableDictionary *item([items objectAtIndex:i]); + for (NSMutableDictionary *item in items) { NSString *label = [item objectForKey:@"label"]; if (label != nil && [label isEqualToString:@"Cydia"]) { cydia = true; @@ -6891,7 +7492,32 @@ id Dealloc_(id self, SEL selector) { }*/ int main(int argc, char *argv[]) { _pooled - bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0; + _trace(); + class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16"); + + bool substrate(false); + + if (argc != 0) { + char **args(argv); + int arge(1); + + for (int argi(1); argi != argc; ++argi) + if (strcmp(argv[argi], "--") == 0) { + arge = argi; + argv[argi] = argv[0]; + argv += argi; + argc -= argi; + break; + } + + for (int argi(1); argi != arge; ++argi) + if (strcmp(args[argi], "--bootstrap") == 0) + bootstrap_ = true; + else if (strcmp(args[argi], "--substrate") == 0) + substrate = true; + else + fprintf(stderr, "unknown argument: %s\n", args[argi]); + } App_ = [[NSBundle mainBundle] bundlePath]; Home_ = NSHomeDirectory(); @@ -6907,10 +7533,12 @@ int main(int argc, char *argv[]) { _pooled setuid(0); setgid(0); +#if 1 /* XXX: this costs 1.4s of startup performance */ if (unlink("/var/cache/apt/pkgcache.bin") == -1) _assert(errno == ENOENT); if (unlink("/var/cache/apt/srcpkgcache.bin") == -1) _assert(errno == ENOENT); +#endif /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc)); alloc_ = alloc->method_imp; @@ -6941,10 +7569,21 @@ int main(int argc, char *argv[]) { _pooled UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier]; + if (NSDictionary *system = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) + Build_ = [system objectForKey:@"ProductBuildVersion"]; + if (NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:@"/Applications/MobileSafari.app/Info.plist"]) { + Product_ = [info objectForKey:@"SafariProductVersion"]; + Safari_ = [info objectForKey:@"CFBundleVersion"]; + } + /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist"); AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/ - if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL) + _trace(); + Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]; + _trace(); + + if (Metadata_ == NULL) Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2]; else { Settings_ = [Metadata_ objectForKey:@"Settings"]; @@ -6976,11 +7615,16 @@ int main(int argc, char *argv[]) { _pooled Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease]; #endif - if (access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0) - dlopen("/Library/MobileSubstrate/MobileSubstrate.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) + if (access("/User", F_OK) != 0) { + _trace(); system("/usr/libexec/cydia/firmware.sh"); + _trace(); + } _assert([[NSFileManager defaultManager] createDirectoryAtPath:@"/var/cache/apt/archives/partial" @@ -6997,6 +7641,19 @@ int main(int argc, char *argv[]) { _pooled Off_.Set(space_, 0.9, 0.9, 0.9, 1.0); White_.Set(space_, 1.0, 1.0, 1.0, 1.0); Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0); + 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]; @@ -7005,6 +7662,7 @@ int main(int argc, char *argv[]) { _pooled UIApplicationUseLegacyEvents(YES); UIKeyboardDisableAutomaticAppearance(); + _trace(); int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia"); CGColorSpaceRelease(space_);