1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #include <CoreGraphics/CoreGraphics.h>
40 #include <GraphicsServices/GraphicsServices.h>
41 #include <Foundation/Foundation.h>
42 #include <UIKit/UIKit.h>
43 #include <WebCore/DOMHTML.h>
48 #include <WebKit/WebFrame.h>
49 #include <WebKit/WebView.h>
51 #include <objc/objc.h>
52 #include <objc/runtime.h>
57 #include <ext/stdio_filebuf.h>
59 #include <apt-pkg/acquire.h>
60 #include <apt-pkg/acquire-item.h>
61 #include <apt-pkg/algorithms.h>
62 #include <apt-pkg/cachefile.h>
63 #include <apt-pkg/configuration.h>
64 #include <apt-pkg/debmetaindex.h>
65 #include <apt-pkg/error.h>
66 #include <apt-pkg/init.h>
67 #include <apt-pkg/pkgrecords.h>
68 #include <apt-pkg/sourcelist.h>
69 #include <apt-pkg/sptr.h>
71 #include <sys/sysctl.h>
75 #include <mach-o/nlist.h>
86 /* Miscellaneous Messages {{{ */
87 @interface NSString (Cydia)
88 - (NSString *) stringByAddingPercentEscapes;
89 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
92 /* External Constants {{{ */
93 extern NSString *kUIButtonBarButtonAction;
94 extern NSString *kUIButtonBarButtonInfo;
95 extern NSString *kUIButtonBarButtonInfoOffset;
96 extern NSString *kUIButtonBarButtonSelectedInfo;
97 extern NSString *kUIButtonBarButtonStyle;
98 extern NSString *kUIButtonBarButtonTag;
99 extern NSString *kUIButtonBarButtonTarget;
100 extern NSString *kUIButtonBarButtonTitle;
101 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
102 extern NSString *kUIButtonBarButtonTitleWidth;
103 extern NSString *kUIButtonBarButtonType;
106 /* iPhoneOS 2.0 Compatibility {{{ */
108 @interface UICGColor : NSObject {
111 - (id) initWithCGColor:(CGColorRef)color;
117 - (UIFont *) fontWithSize:(CGFloat)size;
120 @interface NSObject (iPhoneOS)
121 - (CGColorRef) cgColor;
122 - (CGColorRef) CGColor;
126 @implementation NSObject (iPhoneOS)
128 - (CGColorRef) cgColor {
129 return [self CGColor];
132 - (CGColorRef) CGColor {
133 return (CGColorRef) self;
137 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
142 @interface UITextView (iPhoneOS)
143 - (void) setTextSize:(float)size;
146 @implementation UITextView (iPhoneOS)
148 - (void) setTextSize:(float)size {
149 [self setFont:[[self font] fontWithSize:size]];
156 OBJC_EXPORT const char *class_getName(Class cls);
158 /* Reset View (UIView) {{{ */
159 @interface UIView (RVBook)
160 - (void) resetViewAnimated:(BOOL)animated;
164 @implementation UIView (RVBook)
166 - (void) resetViewAnimated:(BOOL)animated {
167 fprintf(stderr, "%s\n", class_getName(self->isa));
172 fprintf(stderr, "%s\n", class_getName(self->isa));
178 /* Reset View (UITable) {{{ */
179 @interface UITable (RVBook)
180 - (void) resetViewAnimated:(BOOL)animated;
184 @implementation UITable (RVBook)
186 - (void) resetViewAnimated:(BOOL)animated {
187 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
196 /* Reset View (UISectionList) {{{ */
197 @interface UISectionList (RVBook)
198 - (void) resetViewAnimated:(BOOL)animated;
202 @implementation UISectionList (RVBook)
204 - (void) resetViewAnimated:(BOOL)animated {
205 [[self table] resetViewAnimated:animated];
209 [[self table] clearView];
215 /* Perl-Compatible RegEx {{{ */
225 Pcre(const char *regex) :
230 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
233 fprintf(stderr, "%d:%s\n", offset, error);
237 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
238 matches_ = new int[(capture_ + 1) * 3];
246 NSString *operator [](size_t match) {
248 stringWithCString:(data_ + matches_[match * 2])
249 length:(matches_[match * 2 + 1] - matches_[match * 2])
253 bool operator ()(const char *data, size_t size) {
255 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
259 /* Mime Addresses {{{ */
260 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
262 @interface Address : NSObject {
268 - (NSString *) email;
270 + (Address *) addressWithString:(NSString *)string;
271 - (Address *) initWithString:(NSString *)string;
274 @implementation Address
283 - (NSString *) name {
287 - (NSString *) email {
291 + (Address *) addressWithString:(NSString *)string {
292 return [[[Address alloc] initWithString:string] autorelease];
295 - (Address *) initWithString:(NSString *)string {
296 if ((self = [super init]) != nil) {
297 const char *data = [string UTF8String];
298 size_t size = [string length];
300 if (email_r(data, size)) {
301 name_ = [email_r[1] retain];
302 email_ = [email_r[2] retain];
304 name_ = [[NSString stringWithCString:data length:size] retain];
312 /* CoreGraphics Primitives {{{ */
323 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
326 Set(space, red, green, blue, alpha);
331 CGColorRelease(color_);
338 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
340 float color[] = {red, green, blue, alpha};
341 color_ = CGColorCreate(space, color);
344 operator CGColorRef() {
359 /* Right Alignment {{{ */
360 @interface UIRightTextLabel : UITextLabel {
361 float _savedRightEdgeX;
362 BOOL _sizedtofit_flag;
365 - (void) setFrame:(CGRect)frame;
366 - (void) setText:(NSString *)text;
367 - (void) realignText;
370 @implementation UIRightTextLabel
372 - (void) setFrame:(CGRect)frame {
373 [super setFrame:frame];
374 if (_sizedtofit_flag == NO) {
375 _savedRightEdgeX = frame.origin.x;
380 - (void) setText:(NSString *)text {
381 [super setText:text];
385 - (void) realignText {
386 CGRect oldFrame = [self frame];
388 _sizedtofit_flag = YES;
389 [self sizeToFit]; // shrink down size so I can right align it
391 CGRect newFrame = [self frame];
393 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
394 oldFrame.size.width = newFrame.size.width;
395 [super setFrame:oldFrame];
396 _sizedtofit_flag = NO;
402 /* Random Global Variables {{{ */
403 static const int PulseInterval_ = 50000;
405 static CGColor Black_;
406 static CGColor Clear_;
408 static CGColor White_;
410 static NSString *Home_;
411 static BOOL Sounds_Keyboard_;
413 const char *Firmware_ = NULL;
414 const char *Machine_ = NULL;
415 const char *SerialNumber_ = NULL;
421 CGColorSpaceRef space_;
423 #define FW_LEAST(major, minor, bugfix) \
424 (major < Major_ || major == Major_ && \
425 (minor < Minor_ || minor == Minor_ && \
431 static NSMutableDictionary *Metadata_;
432 static NSMutableDictionary *Packages_;
435 NSString *GetLastUpdate() {
436 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
439 return @"Never or Unknown";
441 CFLocaleRef locale = CFLocaleCopyCurrent();
442 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
443 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
445 CFRelease(formatter);
448 return [(NSString *) formatted autorelease];
451 /* Display Helpers {{{ */
452 inline float Interpolate(float begin, float end, float fraction) {
453 return (end - begin) * fraction + begin;
456 NSString *SizeString(double size) {
458 while (size > 1024) {
463 static const char *powers_[] = {"B", "kB", "MB", "GB"};
465 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
468 static const float TextViewOffset_ = 22;
470 UITextView *GetTextView(NSString *value, float left, bool html) {
471 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
472 [text setEditable:NO];
473 [text setTextSize:16];
475 [text setHTML:value];
477 [text setText:value];
478 [text setEnabled:NO];
480 [text setBackgroundColor:Clear_];
482 CGRect frame = [text frame];
483 [text setFrame:frame];
484 CGRect rect = [text visibleTextRect];
485 frame.size.height = rect.size.height;
486 [text setFrame:frame];
491 NSString *Simplify(NSString *title) {
492 const char *data = [title UTF8String];
493 size_t size = [title length];
495 Pcre title_r("^(.*?)( \\(.*\\))?$");
496 if (title_r(data, size))
503 /* Delegate Prototypes {{{ */
507 @protocol ProgressDelegate
508 - (void) setProgressError:(NSString *)error;
509 - (void) setProgressTitle:(NSString *)title;
510 - (void) setProgressPercent:(float)percent;
511 - (void) addProgressOutput:(NSString *)output;
514 @protocol CydiaDelegate
515 - (void) installPackage:(Package *)package;
516 - (void) removePackage:(Package *)package;
517 - (void) slideUp:(UIAlertSheet *)alert;
518 - (void) distUpgrade;
522 /* Status Delegation {{{ */
524 public pkgAcquireStatus
527 _transient id<ProgressDelegate> delegate_;
535 void setDelegate(id delegate) {
536 delegate_ = delegate;
539 virtual bool MediaChange(std::string media, std::string drive) {
543 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
546 virtual void Fetch(pkgAcquire::ItemDesc &item) {
547 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
550 virtual void Done(pkgAcquire::ItemDesc &item) {
553 virtual void Fail(pkgAcquire::ItemDesc &item) {
555 item.Owner->Status == pkgAcquire::Item::StatIdle ||
556 item.Owner->Status == pkgAcquire::Item::StatDone
560 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
563 virtual bool Pulse(pkgAcquire *Owner) {
564 bool value = pkgAcquireStatus::Pulse(Owner);
567 double(CurrentBytes + CurrentItems) /
568 double(TotalBytes + TotalItems)
571 [delegate_ setProgressPercent:percent];
575 virtual void Start() {
578 virtual void Stop() {
582 /* Progress Delegation {{{ */
587 _transient id<ProgressDelegate> delegate_;
590 virtual void Update() {
591 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
592 [delegate_ setProgressPercent:(Percent / 100)];
601 void setDelegate(id delegate) {
602 delegate_ = delegate;
605 virtual void Done() {
606 [delegate_ setProgressPercent:1];
611 /* Database Interface {{{ */
612 @interface Database : NSObject {
614 pkgRecords *records_;
615 pkgProblemResolver *resolver_;
616 pkgAcquire *fetcher_;
618 SPtr<pkgPackageManager> manager_;
619 pkgSourceList *list_;
621 NSMutableDictionary *sources_;
622 NSMutableArray *packages_;
624 _transient id delegate_;
630 - (void) _readStatus:(NSNumber *)fd;
631 - (void) _readOutput:(NSNumber *)fd;
633 - (Package *) packageWithName:(NSString *)name;
636 - (pkgCacheFile &) cache;
637 - (pkgRecords *) records;
638 - (pkgProblemResolver *) resolver;
639 - (pkgAcquire &) fetcher;
640 - (NSArray *) packages;
648 - (void) updateWithStatus:(Status &)status;
650 - (void) setDelegate:(id)delegate;
651 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
655 /* Source Class {{{ */
656 @interface Source : NSObject {
657 NSString *description_;
662 NSString *distribution_;
666 NSString *defaultIcon_;
671 - (Source *) initWithMetaIndex:(metaIndex *)index;
676 - (NSString *) distribution;
679 - (NSString *) description;
680 - (NSString *) label;
681 - (NSString *) origin;
682 - (NSString *) version;
684 - (NSString *) defaultIcon;
687 @implementation Source
691 [distribution_ release];
694 if (description_ != nil)
695 [description_ release];
702 if (defaultIcon_ != nil)
703 [defaultIcon_ release];
708 - (Source *) initWithMetaIndex:(metaIndex *)index {
709 if ((self = [super init]) != nil) {
710 trusted_ = index->IsTrusted();
712 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
713 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
714 type_ = [[NSString stringWithCString:index->GetType()] retain];
722 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
723 if (dindex != NULL) {
724 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
726 while (std::getline(release, line)) {
727 std::string::size_type colon(line.find(':'));
728 if (colon == std::string::npos)
731 std::string name(line.substr(0, colon));
732 std::string value(line.substr(colon + 1));
733 while (!value.empty() && value[0] == ' ')
734 value = value.substr(1);
736 if (name == "Default-Icon")
737 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
738 else if (name == "Description")
739 description_ = [[NSString stringWithCString:value.c_str()] retain];
740 else if (name == "Label")
741 label_ = [[NSString stringWithCString:value.c_str()] retain];
742 else if (name == "Origin")
743 origin_ = [[NSString stringWithCString:value.c_str()] retain];
744 else if (name == "Version")
745 version_ = [[NSString stringWithCString:value.c_str()] retain];
759 - (NSString *) distribution {
760 return distribution_;
763 - (NSString *) type {
767 - (NSString *) description {
771 - (NSString *) label {
775 - (NSString *) origin {
779 - (NSString *) version {
783 - (NSString *) defaultIcon {
789 /* Package Class {{{ */
790 NSString *Scour(const char *field, const char *begin, const char *end) {
791 size_t i(0), l(strlen(field));
794 const char *name = begin + i;
795 const char *colon = name + l;
796 const char *value = colon + 1;
801 memcmp(name, field, l) == 0
803 while (value != end && value[0] == ' ')
805 const char *line = std::find(value, end, '\n');
806 while (line != value && line[-1] == ' ')
808 return [NSString stringWithCString:value length:(line - value)];
810 begin = std::find(begin, end, '\n');
818 @interface Package : NSObject {
819 pkgCache::PkgIterator iterator_;
820 _transient Database *database_;
821 pkgCache::VerIterator version_;
822 pkgCache::VerFileIterator file_;
826 NSString *installed_;
835 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
836 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
838 - (NSString *) section;
839 - (Address *) maintainer;
841 - (NSString *) description;
842 - (NSString *) index;
846 - (NSString *) latest;
847 - (NSString *) installed;
854 - (NSString *) tagline;
856 - (NSString *) website;
860 - (BOOL) matches:(NSString *)text;
862 - (NSComparisonResult) compareByName:(Package *)package;
863 - (NSComparisonResult) compareBySection:(Package *)package;
864 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
865 - (NSComparisonResult) compareForChanges:(Package *)package;
870 - (NSNumber *) isSearchedForBy:(NSString *)search;
871 - (NSNumber *) isInstalledInSection:(NSString *)section;
872 - (NSNumber *) isUninstalledInSection:(NSString *)section;
876 @implementation Package
880 if (installed_ != nil)
881 [installed_ release];
897 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
898 if ((self = [super init]) != nil) {
899 iterator_ = iterator;
900 database_ = database;
905 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
907 const char *begin, *end;
908 parser->GetRec(begin, end);
910 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
911 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
913 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
914 name_ = Scour("Name", begin, end);
916 name_ = [name_ retain];
917 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
918 icon_ = Scour("Icon", begin, end);
920 icon_ = [icon_ retain];
921 website_ = Scour("Website", begin, end);
923 website_ = [website_ retain];
925 source_ = [[database_ getSource:file_.File()] retain];
927 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
928 if (metadata == nil || [metadata count] == 0) {
929 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
933 [Packages_ setObject:metadata forKey:id_];
938 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
939 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
940 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
941 return [[[Package alloc]
942 initWithIterator:iterator
950 - (NSString *) section {
951 const char *section = iterator_.Section();
952 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
955 - (Address *) maintainer {
956 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
957 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
961 return version_->InstalledSize;
964 - (NSString *) description {
965 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
966 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
968 NSArray *lines = [description componentsSeparatedByString:@"\n"];
969 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
970 if ([lines count] < 2)
973 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
974 for (size_t i(1); i != [lines count]; ++i) {
975 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
976 [trimmed addObject:trim];
979 return [trimmed componentsJoinedByString:@"\n"];
982 - (NSString *) index {
983 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
984 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
988 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
991 - (NSString *) latest {
995 - (NSString *) installed {
999 - (BOOL) upgradable {
1000 if (NSString *installed = [self installed])
1001 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1003 return [self essential];
1006 - (BOOL) essential {
1007 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1011 return (*[database_ cache])[iterator_].InstBroken();
1018 - (NSString *) name {
1019 return name_ == nil ? id_ : name_;
1022 - (NSString *) tagline {
1026 - (NSString *) icon {
1030 - (NSString *) website {
1034 - (Source *) source {
1038 - (BOOL) matches:(NSString *)text {
1044 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1045 if (range.location != NSNotFound)
1048 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1049 if (range.location != NSNotFound)
1052 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1053 if (range.location != NSNotFound)
1059 - (NSComparisonResult) compareByName:(Package *)package {
1060 NSString *lhs = [self name];
1061 NSString *rhs = [package name];
1063 if ([lhs length] != 0 && [rhs length] != 0) {
1064 unichar lhc = [lhs characterAtIndex:0];
1065 unichar rhc = [rhs characterAtIndex:0];
1067 if (isalpha(lhc) && !isalpha(rhc))
1068 return NSOrderedAscending;
1069 else if (!isalpha(lhc) && isalpha(rhc))
1070 return NSOrderedDescending;
1073 return [lhs caseInsensitiveCompare:rhs];
1076 - (NSComparisonResult) compareBySection:(Package *)package {
1077 NSString *lhs = [self section];
1078 NSString *rhs = [package section];
1080 if (lhs == NULL && rhs != NULL)
1081 return NSOrderedAscending;
1082 else if (lhs != NULL && rhs == NULL)
1083 return NSOrderedDescending;
1084 else if (lhs != NULL && rhs != NULL) {
1085 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1086 if (result != NSOrderedSame)
1090 return NSOrderedSame;
1093 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1094 NSString *lhs = [self section];
1095 NSString *rhs = [package section];
1097 if (lhs == NULL && rhs != NULL)
1098 return NSOrderedAscending;
1099 else if (lhs != NULL && rhs == NULL)
1100 return NSOrderedDescending;
1101 else if (lhs != NULL && rhs != NULL) {
1102 NSComparisonResult result = [lhs compare:rhs];
1103 if (result != NSOrderedSame)
1107 return [self compareByName:package];
1110 - (NSComparisonResult) compareForChanges:(Package *)package {
1111 BOOL lhs = [self upgradable];
1112 BOOL rhs = [package upgradable];
1115 return lhs ? NSOrderedAscending : NSOrderedDescending;
1117 switch ([[self seen] compare:[package seen]]) {
1118 case NSOrderedAscending:
1119 return NSOrderedDescending;
1122 case NSOrderedDescending:
1123 return NSOrderedAscending;
1129 return [self compareByName:package];
1133 pkgProblemResolver *resolver = [database_ resolver];
1134 resolver->Clear(iterator_);
1135 resolver->Protect(iterator_);
1136 pkgCacheFile &cache([database_ cache]);
1137 cache->MarkInstall(iterator_, false);
1138 pkgDepCache::StateCache &state((*cache)[iterator_]);
1139 if (!state.Install())
1140 cache->SetReInstall(iterator_, true);
1144 pkgProblemResolver *resolver = [database_ resolver];
1145 resolver->Clear(iterator_);
1146 resolver->Protect(iterator_);
1147 resolver->Remove(iterator_);
1148 [database_ cache]->MarkDelete(iterator_, true);
1151 - (NSNumber *) isSearchedForBy:(NSString *)search {
1152 return [NSNumber numberWithBool:[self matches:search]];
1155 - (NSNumber *) isInstalledInSection:(NSString *)section {
1156 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1159 - (NSNumber *) isUninstalledInSection:(NSString *)section {
1160 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1165 /* Section Class {{{ */
1166 @interface Section : NSObject {
1172 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1173 - (NSString *) name;
1176 - (void) addToCount;
1180 @implementation Section
1187 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1188 if ((self = [super init]) != nil) {
1189 name_ = [name retain];
1194 - (NSString *) name {
1206 - (void) addToCount {
1213 /* Database Implementation {{{ */
1214 @implementation Database
1221 - (void) _readStatus:(NSNumber *)fd {
1222 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1224 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1225 std::istream is(&ib);
1230 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1232 pcre_extra *study = NULL;
1234 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1235 int matches[(capture + 1) * 3];
1237 while (std::getline(is, line)) {
1238 const char *data(line.c_str());
1240 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1242 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1245 [delegate_ setProgressPercent:(percent / 100)];
1247 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1248 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1250 if (type == "pmerror")
1251 [delegate_ setProgressError:string];
1252 else if (type == "pmstatus")
1253 [delegate_ setProgressTitle:string];
1254 else if (type == "pmconffile")
1256 else _assert(false);
1263 - (void) _readOutput:(NSNumber *)fd {
1264 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1266 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1267 std::istream is(&ib);
1270 while (std::getline(is, line))
1271 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1277 - (Package *) packageWithName:(NSString *)name {
1278 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1279 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1282 - (Database *) init {
1283 if ((self = [super init]) != nil) {
1289 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1290 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1294 _assert(pipe(fds) != -1);
1298 detachNewThreadSelector:@selector(_readStatus:)
1300 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1303 _assert(pipe(fds) != -1);
1304 _assert(dup2(fds[1], 1) != -1);
1305 _assert(close(fds[1]) != -1);
1308 detachNewThreadSelector:@selector(_readOutput:)
1310 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1315 - (pkgCacheFile &) cache {
1319 - (pkgRecords *) records {
1323 - (pkgProblemResolver *) resolver {
1327 - (pkgAcquire &) fetcher {
1331 - (NSArray *) packages {
1335 - (void) reloadData {
1345 if (!cache_.Open(progress_, true)) {
1346 fprintf(stderr, "repairing corrupted database...\n");
1348 [self updateWithStatus:status_];
1349 _assert(cache_.Open(progress_, true));
1352 now_ = [[NSDate date] retain];
1354 records_ = new pkgRecords(cache_);
1355 resolver_ = new pkgProblemResolver(cache_);
1356 fetcher_ = new pkgAcquire(&status_);
1359 list_ = new pkgSourceList();
1360 _assert(list_->ReadMainList());
1362 [sources_ removeAllObjects];
1363 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1364 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1365 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1367 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1368 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1372 [packages_ removeAllObjects];
1373 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1374 if (Package *package = [Package packageWithIterator:iterator database:self])
1375 if ([package source] != nil || [package installed] != nil)
1376 [packages_ addObject:package];
1380 pkgRecords records(cache_);
1382 lock_ = new FileFd();
1383 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1384 _assert(!_error->PendingError());
1387 // XXX: explain this with an error message
1388 _assert(list.ReadMainList());
1390 manager_ = (_system->CreatePM(cache_));
1391 _assert(manager_->GetArchives(fetcher_, &list, &records));
1392 _assert(!_error->PendingError());
1396 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1398 _assert(list.ReadMainList());
1399 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1400 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1403 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1407 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1409 if (result == pkgPackageManager::Failed)
1411 if (_error->PendingError())
1413 if (result != pkgPackageManager::Completed)
1416 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1418 _assert(list.ReadMainList());
1419 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1420 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1423 if (![before isEqualToArray:after])
1428 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1429 _assert(pkgApplyStatus(cache_));
1431 if (cache_->BrokenCount() != 0) {
1432 _assert(pkgFixBroken(cache_));
1433 _assert(cache_->BrokenCount() == 0);
1434 _assert(pkgMinimizeUpgrade(cache_));
1437 _assert(pkgDistUpgrade(cache_));
1441 [self updateWithStatus:status_];
1444 - (void) updateWithStatus:(Status &)status {
1446 _assert(list.ReadMainList());
1449 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1450 _assert(!_error->PendingError());
1452 pkgAcquire fetcher(&status);
1453 _assert(list.GetIndexes(&fetcher));
1455 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1456 bool failed = false;
1457 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1458 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1459 (*item)->Finished();
1463 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1464 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1465 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1468 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1472 - (void) setDelegate:(id)delegate {
1473 delegate_ = delegate;
1474 status_.setDelegate(delegate);
1475 progress_.setDelegate(delegate);
1478 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1479 pkgIndexFile *index(NULL);
1480 list_->FindIndex(file, index);
1481 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1487 /* Confirmation View {{{ */
1488 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1489 if ([packages count] == 0)
1492 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1493 [fields setObject:text forKey:key];
1495 CGColor blue(space_, 0, 0, 0.4, 1);
1496 [text setTextColor:blue];
1499 @protocol ConfirmationViewDelegate
1504 @interface ConfirmationView : UIView {
1505 Database *database_;
1507 UITransitionView *transition_;
1509 UINavigationBar *navbar_;
1510 UIPreferencesTable *table_;
1511 NSMutableDictionary *fields_;
1512 UIAlertSheet *essential_;
1517 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1521 @implementation ConfirmationView
1524 [navbar_ setDelegate:nil];
1525 [transition_ setDelegate:nil];
1526 [table_ setDataSource:nil];
1528 [transition_ release];
1533 if (essential_ != nil)
1534 [essential_ release];
1539 [transition_ transition:7 toView:nil];
1543 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1544 if (from != nil && to == nil)
1545 [self removeFromSuperview];
1548 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1551 if (essential_ != nil)
1552 [essential_ popupAlertAnimated:YES];
1554 [delegate_ confirm];
1563 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1564 [essential_ dismiss];
1568 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1572 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1574 case 0: return @"Statistics";
1575 case 1: return @"Modifications";
1577 default: _assert(false);
1581 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1584 case 1: return [fields_ count];
1586 default: _assert(false);
1590 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1591 if (group != 1 || row == -1)
1594 _assert(size_t(row) < [fields_ count]);
1595 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1599 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1600 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1601 [cell setShowSelection:NO];
1604 case 0: switch (row) {
1606 [cell setTitle:@"Downloading"];
1607 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1611 [cell setTitle:@"Resuming At"];
1612 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1616 double size([database_ cache]->UsrSize());
1619 [cell setTitle:@"Disk Freeing"];
1620 [cell setValue:SizeString(-size)];
1622 [cell setTitle:@"Disk Using"];
1623 [cell setValue:SizeString(size)];
1627 default: _assert(false);
1631 _assert(size_t(row) < [fields_ count]);
1632 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1633 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1636 default: _assert(false);
1642 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1643 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1644 database_ = database;
1645 delegate_ = delegate;
1647 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1648 [self addSubview:transition_];
1650 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1652 CGSize navsize = [UINavigationBar defaultSize];
1653 CGRect navrect = {{0, 0}, navsize};
1654 CGRect bounds = [overlay_ bounds];
1656 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1657 [navbar_ setBarStyle:1];
1658 [navbar_ setDelegate:self];
1660 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1661 [navbar_ pushNavigationItem:navitem];
1662 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1664 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1666 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1667 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1668 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1669 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1670 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
1674 pkgCacheFile &cache([database_ cache]);
1675 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
1676 Package *package([Package packageWithIterator:iterator database:database_]);
1677 NSString *name([package name]);
1678 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
1679 pkgDepCache::StateCache &state(cache[iterator]);
1681 if (state.NewInstall())
1682 [installing addObject:name];
1683 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
1684 [reinstalling addObject:name];
1685 else if (state.Upgrade())
1686 [upgrading addObject:name];
1687 else if (state.Downgrade())
1688 [downgrading addObject:name];
1689 else if (state.Delete()) {
1692 [removing addObject:name];
1699 essential_ = [[UIAlertSheet alloc]
1700 initWithTitle:@"Unable to Comply"
1701 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1702 defaultButtonIndex:0
1707 [essential_ setBodyText:@"One or more of the packages you are about to remove are marked 'Essential' and cannot be removed by Cydia. Please use apt-get."];
1710 AddTextView(fields_, installing, @"Installing");
1711 AddTextView(fields_, reinstalling, @"Reinstalling");
1712 AddTextView(fields_, upgrading, @"Upgrading");
1713 AddTextView(fields_, downgrading, @"Downgrading");
1714 AddTextView(fields_, removing, @"Removing");
1716 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
1717 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
1720 [table_ setReusesTableCells:YES];
1721 [table_ setDataSource:self];
1722 [table_ reloadData];
1724 [overlay_ addSubview:navbar_];
1725 [overlay_ addSubview:table_];
1727 [view addSubview:self];
1729 [transition_ setDelegate:self];
1731 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
1732 [transition_ transition:0 toView:blank];
1733 [transition_ transition:3 toView:overlay_];
1740 /* Progress Data {{{ */
1741 @interface ProgressData : NSObject {
1747 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
1754 @implementation ProgressData
1756 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
1757 if ((self = [super init]) != nil) {
1758 selector_ = selector;
1778 /* Progress View {{{ */
1779 @interface ProgressView : UIView <
1783 UIView *background_;
1784 UITransitionView *transition_;
1786 UINavigationBar *navbar_;
1787 UIProgressBar *progress_;
1788 UITextView *output_;
1789 UITextLabel *status_;
1793 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
1795 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
1796 - (void) setContentView:(UIView *)view;
1799 - (void) _retachThread;
1800 - (void) _detachNewThreadData:(ProgressData *)data;
1801 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
1805 @protocol ProgressViewDelegate
1806 - (void) progressViewIsComplete:(ProgressView *)sender;
1809 @implementation ProgressView
1812 [transition_ setDelegate:nil];
1813 [navbar_ setDelegate:nil];
1816 if (background_ != nil)
1817 [background_ release];
1818 [transition_ release];
1821 [progress_ release];
1827 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1828 if (bootstrap_ && from == overlay_ && to == view_)
1832 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
1833 if ((self = [super initWithFrame:frame]) != nil) {
1834 delegate_ = delegate;
1836 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1837 [transition_ setDelegate:self];
1839 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1842 [overlay_ setBackgroundColor:Black_];
1844 background_ = [[UIView alloc] initWithFrame:[self bounds]];
1845 [background_ setBackgroundColor:Black_];
1846 [self addSubview:background_];
1849 [self addSubview:transition_];
1851 CGSize navsize = [UINavigationBar defaultSize];
1852 CGRect navrect = {{0, 0}, navsize};
1854 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1855 [overlay_ addSubview:navbar_];
1857 [navbar_ setBarStyle:1];
1858 [navbar_ setDelegate:self];
1860 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
1861 [navbar_ pushNavigationItem:navitem];
1863 CGRect bounds = [overlay_ bounds];
1864 CGSize prgsize = [UIProgressBar defaultSize];
1867 (bounds.size.width - prgsize.width) / 2,
1868 bounds.size.height - prgsize.height - 20
1871 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
1872 [overlay_ addSubview:progress_];
1874 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
1876 bounds.size.height - prgsize.height - 50,
1877 bounds.size.width - 20,
1881 [status_ setColor:White_];
1882 [status_ setBackgroundColor:Clear_];
1884 [status_ setCentersHorizontally:YES];
1885 //[status_ setFont:font];
1887 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
1889 navrect.size.height + 20,
1890 bounds.size.width - 20,
1891 bounds.size.height - navsize.height - 62 - navrect.size.height
1894 //[output_ setTextFont:@"Courier New"];
1895 [output_ setTextSize:12];
1897 [output_ setTextColor:White_];
1898 [output_ setBackgroundColor:Clear_];
1900 [output_ setMarginTop:0];
1901 [output_ setAllowsRubberBanding:YES];
1903 [overlay_ addSubview:output_];
1904 [overlay_ addSubview:status_];
1906 [progress_ setStyle:0];
1910 - (void) setContentView:(UIView *)view {
1911 view_ = [view retain];
1914 - (void) resetView {
1915 [transition_ transition:6 toView:view_];
1918 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1922 - (void) _retachThread {
1923 [delegate_ progressViewIsComplete:self];
1927 - (void) _detachNewThreadData:(ProgressData *)data {
1928 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1930 [[data target] performSelector:[data selector] withObject:[data object]];
1933 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
1938 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
1939 [navbar_ popNavigationItem];
1940 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1941 [navbar_ pushNavigationItem:navitem];
1943 [status_ setText:nil];
1944 [output_ setText:@""];
1945 [progress_ setProgress:0];
1947 [transition_ transition:6 toView:overlay_];
1950 detachNewThreadSelector:@selector(_detachNewThreadData:)
1952 withObject:[[ProgressData alloc]
1953 initWithSelector:selector
1960 - (void) setProgressError:(NSString *)error {
1962 performSelectorOnMainThread:@selector(_setProgressError:)
1968 - (void) setProgressTitle:(NSString *)title {
1970 performSelectorOnMainThread:@selector(_setProgressTitle:)
1976 - (void) setProgressPercent:(float)percent {
1978 performSelectorOnMainThread:@selector(_setProgressPercent:)
1979 withObject:[NSNumber numberWithFloat:percent]
1984 - (void) addProgressOutput:(NSString *)output {
1986 performSelectorOnMainThread:@selector(_addProgressOutput:)
1992 - (void) _setProgressError:(NSString *)error {
1993 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
1994 initWithTitle:@"Package Error"
1995 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1996 defaultButtonIndex:0
2001 [sheet setBodyText:error];
2002 [sheet popupAlertAnimated:YES];
2005 - (void) _setProgressTitle:(NSString *)title {
2006 [status_ setText:[title stringByAppendingString:@"..."]];
2009 - (void) _setProgressPercent:(NSNumber *)percent {
2010 [progress_ setProgress:[percent floatValue]];
2013 - (void) _addProgressOutput:(NSString *)output {
2014 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2015 CGSize size = [output_ contentSize];
2016 CGRect rect = {{0, size.height}, {size.width, 0}};
2017 [output_ scrollRectToVisible:rect animated:YES];
2023 /* Package Cell {{{ */
2024 @interface PackageCell : UITableCell {
2027 UITextLabel *description_;
2028 UITextLabel *source_;
2029 UIImageView *trusted_;
2032 - (PackageCell *) init;
2033 - (void) setPackage:(Package *)package;
2035 - (void) _setSelected:(float)fraction;
2036 - (void) setSelected:(BOOL)selected;
2037 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2038 - (void) _setSelectionFadeFraction:(float)fraction;
2042 @implementation PackageCell
2047 [description_ release];
2053 - (PackageCell *) init {
2054 if ((self = [super init]) != nil) {
2055 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2056 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2057 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2059 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2061 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2062 [name_ setBackgroundColor:Clear_];
2063 [name_ setFont:bold];
2065 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2066 [source_ setBackgroundColor:Clear_];
2067 [source_ setFont:large];
2069 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2070 [description_ setBackgroundColor:Clear_];
2071 [description_ setFont:small];
2073 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2074 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
2076 [self addSubview:icon_];
2077 [self addSubview:name_];
2078 [self addSubview:description_];
2079 [self addSubview:source_];
2087 - (void) setPackage:(Package *)package {
2088 Source *source = [package source];
2090 UIImage *image = nil;
2091 if (NSString *icon = [package icon])
2092 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2093 if (image == nil) if (NSString *icon = [source defaultIcon])
2094 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2096 image = [UIImage applicationImageNamed:@"unknown.png"];
2097 [icon_ setImage:image];
2100 CGSize size = [image size];
2101 float scale = 30 / std::max(size.width, size.height);
2102 [icon_ zoomToScale:scale];
2105 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2107 [name_ setText:[package name]];
2108 [description_ setText:[package tagline]];
2113 if (source != nil) {
2114 label = [source label];
2115 trusted = [source trusted];
2116 } else if ([[package id] isEqualToString:@"firmware"]) {
2120 label = @"Unknown/Local";
2124 [source_ setText:[NSString stringWithFormat:@"from %@ (%@)", label, Simplify([package section])]];
2127 [self addSubview:trusted_];
2129 [trusted_ removeFromSuperview];
2132 - (void) _setSelected:(float)fraction {
2133 CGColor black(space_,
2134 Interpolate(0.0, 1.0, fraction),
2135 Interpolate(0.0, 1.0, fraction),
2136 Interpolate(0.0, 1.0, fraction),
2139 CGColor gray(space_,
2140 Interpolate(0.4, 1.0, fraction),
2141 Interpolate(0.4, 1.0, fraction),
2142 Interpolate(0.4, 1.0, fraction),
2145 [name_ setColor:black];
2146 [description_ setColor:gray];
2147 [source_ setColor:black];
2150 - (void) setSelected:(BOOL)selected {
2151 [self _setSelected:(selected ? 1.0 : 0.0)];
2152 [super setSelected:selected];
2155 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2157 [self _setSelected:(selected ? 1.0 : 0.0)];
2158 [super setSelected:selected withFade:fade];
2161 - (void) _setSelectionFadeFraction:(float)fraction {
2162 [self _setSelected:fraction];
2163 [super _setSelectionFadeFraction:fraction];
2168 /* Section Cell {{{ */
2169 @interface SectionCell : UITableCell {
2171 UITextLabel *count_;
2175 - (void) setSection:(Section *)section;
2177 - (void) _setSelected:(float)fraction;
2178 - (void) setSelected:(BOOL)selected;
2179 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2180 - (void) _setSelectionFadeFraction:(float)fraction;
2184 @implementation SectionCell
2193 if ((self = [super init]) != nil) {
2194 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2195 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2197 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2198 [name_ setBackgroundColor:Clear_];
2199 [name_ setFont:bold];
2201 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2202 [count_ setCentersHorizontally:YES];
2203 [count_ setBackgroundColor:Clear_];
2204 [count_ setFont:small];
2205 [count_ setColor:White_];
2207 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2208 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2210 [self addSubview:folder];
2211 [self addSubview:name_];
2212 [self addSubview:count_];
2214 [self _setSelected:0];
2221 - (void) setSection:(Section *)section {
2222 if (section == nil) {
2223 [name_ setText:@"All Packages"];
2224 [count_ setText:nil];
2226 NSString *name = [section name];
2227 [name_ setText:(name == nil ? @"(No Section)" : name)];
2228 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2232 - (void) _setSelected:(float)fraction {
2233 CGColor black(space_,
2234 Interpolate(0.0, 1.0, fraction),
2235 Interpolate(0.0, 1.0, fraction),
2236 Interpolate(0.0, 1.0, fraction),
2239 [name_ setColor:black];
2242 - (void) setSelected:(BOOL)selected {
2243 [self _setSelected:(selected ? 1.0 : 0.0)];
2244 [super setSelected:selected];
2247 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2249 [self _setSelected:(selected ? 1.0 : 0.0)];
2250 [super setSelected:selected withFade:fade];
2253 - (void) _setSelectionFadeFraction:(float)fraction {
2254 [self _setSelected:fraction];
2255 [super _setSelectionFadeFraction:fraction];
2261 /* Browser Interface {{{ */
2262 @interface BrowserView : RVPage {
2263 _transient Database *database_;
2264 UIScroller *scroller_;
2265 UIWebView *webview_;
2266 NSMutableArray *urls_;
2267 UIProgressIndicator *indicator_;
2274 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2275 - (void) loadURL:(NSURL *)url;
2277 - (void) loadRequest:(NSURLRequest *)request;
2280 - (WebView *) webView;
2282 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2287 /* Package View {{{ */
2288 @protocol PackageViewDelegate
2289 - (void) performPackage:(Package *)package;
2292 @interface PackageView : RVPage {
2293 _transient Database *database_;
2294 UIPreferencesTable *table_;
2297 UITextView *description_;
2300 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2301 - (void) setPackage:(Package *)package;
2305 @implementation PackageView
2308 [table_ setDataSource:nil];
2309 [table_ setDelegate:nil];
2311 if (package_ != nil)
2315 if (description_ != nil)
2316 [description_ release];
2321 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2322 return [package_ source] == nil ? 2 : 3;
2325 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2328 case 1: return @"Package Details";
2329 case 2: return @"Source Information";
2331 default: _assert(false);
2335 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2336 if (group != 0 || row != 1)
2339 return [description_ visibleTextRect].size.height + TextViewOffset_;
2342 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2344 case 0: return [package_ website] == nil ? 2 : 3;
2348 default: _assert(false);
2352 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2353 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2354 [cell setShowSelection:NO];
2357 case 0: switch (row) {
2359 [cell setTitle:[package_ name]];
2360 [cell setValue:[package_ latest]];
2364 [cell addSubview:description_];
2368 [cell setTitle:@"More Information"];
2369 [cell setShowDisclosure:YES];
2370 [cell setShowSelection:YES];
2373 default: _assert(false);
2376 case 1: switch (row) {
2378 [cell setTitle:@"Identifier"];
2379 [cell setValue:[package_ id]];
2383 [cell setTitle:@"Installed Version"];
2384 NSString *installed([package_ installed]);
2385 [cell setValue:(installed == nil ? @"n/a" : installed)];
2389 [cell setTitle:@"Section"];
2390 NSString *section([package_ section]);
2391 [cell setValue:(section == nil ? @"n/a" : section)];
2395 [cell setTitle:@"Expanded Size"];
2396 [cell setValue:SizeString([package_ size])];
2400 [cell setTitle:@"Maintainer"];
2401 [cell setValue:[[package_ maintainer] name]];
2402 [cell setShowDisclosure:YES];
2403 [cell setShowSelection:YES];
2406 default: _assert(false);
2409 case 2: switch (row) {
2411 [cell setTitle:[[package_ source] label]];
2412 [cell setValue:[[package_ source] version]];
2416 [cell setValue:[[package_ source] description]];
2420 [cell setTitle:@"Origin"];
2421 [cell setValue:[[package_ source] origin]];
2424 default: _assert(false);
2427 default: _assert(false);
2433 - (BOOL) canSelectRow:(int)row {
2437 - (void) tableRowSelected:(NSNotification *)notification {
2438 int row = [table_ selectedRow];
2439 NSString *website = [package_ website];
2441 if (row == (website == nil ? 8 : 9))
2442 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2443 [[package_ maintainer] email],
2444 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2446 else if (website != nil && row == 3) {
2447 NSURL *url = [NSURL URLWithString:website];
2448 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2449 [browser setDelegate:delegate_];
2450 [book_ pushPage:browser];
2451 [browser loadURL:url];
2455 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2457 case 1: [delegate_ installPackage:package_]; break;
2458 case 2: [delegate_ removePackage:package_]; break;
2464 - (void) _rightButtonClicked {
2465 if ([package_ installed] == nil)
2466 [delegate_ installPackage:package_];
2468 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2470 if ([package_ upgradable])
2471 [buttons addObject:@"Upgrade"];
2473 [buttons addObject:@"Reinstall"];
2475 [buttons addObject:@"Remove"];
2476 [buttons addObject:@"Cancel"];
2478 [delegate_ slideUp:[[[UIAlertSheet alloc]
2479 initWithTitle:@"Manage Package"
2481 defaultButtonIndex:2
2488 - (NSString *) rightButtonTitle {
2489 _assert(package_ != nil);
2490 return [package_ installed] == nil ? @"Install" : @"Modify";
2493 - (NSString *) title {
2497 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2498 if ((self = [super initWithBook:book]) != nil) {
2499 database_ = database;
2501 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2502 [self addSubview:table_];
2504 [table_ setDataSource:self];
2505 [table_ setDelegate:self];
2509 - (void) setPackage:(Package *)package {
2510 if (package_ != nil) {
2511 [package_ autorelease];
2520 if (description_ != nil) {
2521 [description_ release];
2525 if (package != nil) {
2526 package_ = [package retain];
2527 name_ = [[package id] retain];
2529 NSString *description([package description]);
2530 if (description == nil)
2531 description = [package tagline];
2532 description_ = [GetTextView(description, 12, true) retain];
2534 [description_ setTextColor:Black_];
2536 [table_ reloadData];
2540 - (void) resetViewAnimated:(BOOL)animated {
2541 [table_ resetViewAnimated:animated];
2544 - (void) reloadData {
2545 [self setPackage:[database_ packageWithName:name_]];
2546 [self reloadButtons];
2551 /* Package Table {{{ */
2552 @interface PackageTable : RVPage {
2553 _transient Database *database_;
2557 NSMutableArray *packages_;
2558 NSMutableArray *sections_;
2559 UISectionList *list_;
2562 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2564 - (void) setDelegate:(id)delegate;
2565 - (void) setObject:(id)object;
2567 - (void) reloadData;
2568 - (void) resetCursor;
2572 @implementation PackageTable
2575 [list_ setDataSource:nil];
2580 [packages_ release];
2581 [sections_ release];
2586 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2587 return [sections_ count];
2590 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2591 return [[sections_ objectAtIndex:section] name];
2594 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2595 return [[sections_ objectAtIndex:section] row];
2598 - (int) numberOfRowsInTable:(UITable *)table {
2599 return [packages_ count];
2602 - (float) table:(UITable *)table heightForRow:(int)row {
2606 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2608 reusing = [[[PackageCell alloc] init] autorelease];
2609 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2613 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2617 - (void) tableRowSelected:(NSNotification *)notification {
2618 int row = [[notification object] selectedRow];
2622 Package *package = [packages_ objectAtIndex:row];
2623 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2624 [view setDelegate:delegate_];
2625 [view setPackage:package];
2626 [book_ pushPage:view];
2629 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2630 if ((self = [super initWithBook:book]) != nil) {
2631 database_ = database;
2632 title_ = [title retain];
2634 object_ = object == nil ? nil : [object retain];
2636 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2637 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2639 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2640 [list_ setDataSource:self];
2642 UITableColumn *column = [[[UITableColumn alloc]
2643 initWithTitle:@"Name"
2645 width:[self frame].size.width
2648 UITable *table = [list_ table];
2649 [table setSeparatorStyle:1];
2650 [table addTableColumn:column];
2651 [table setDelegate:self];
2652 [table setReusesTableCells:YES];
2654 [self addSubview:list_];
2659 - (void) setDelegate:(id)delegate {
2660 delegate_ = delegate;
2663 - (void) setObject:(id)object {
2669 object_ = [object retain];
2672 - (void) reloadData {
2673 NSArray *packages = [database_ packages];
2675 [packages_ removeAllObjects];
2676 [sections_ removeAllObjects];
2678 for (size_t i(0); i != [packages count]; ++i) {
2679 Package *package([packages objectAtIndex:i]);
2680 if ([[package performSelector:filter_ withObject:object_] boolValue])
2681 [packages_ addObject:package];
2684 [packages_ sortUsingSelector:@selector(compareByName:)];
2686 Section *section = nil;
2688 for (size_t offset(0); offset != [packages_ count]; ++offset) {
2689 Package *package = [packages_ objectAtIndex:offset];
2690 NSString *name = [package index];
2692 if (section == nil || ![[section name] isEqualToString:name]) {
2693 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2694 [sections_ addObject:section];
2697 [section addToCount];
2703 - (NSString *) title {
2707 - (void) resetViewAnimated:(BOOL)animated {
2708 [list_ resetViewAnimated:animated];
2711 - (void) resetCursor {
2712 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
2718 /* Browser Implementation {{{ */
2719 @implementation BrowserView
2722 WebView *webview = [webview_ webView];
2723 [webview setFrameLoadDelegate:nil];
2724 [webview setResourceLoadDelegate:nil];
2725 [webview setUIDelegate:nil];
2727 [scroller_ setDelegate:nil];
2728 [webview_ setDelegate:nil];
2730 [scroller_ release];
2733 [indicator_ release];
2739 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
2740 NSMutableURLRequest *request = [NSMutableURLRequest
2743 timeoutInterval:30.0
2746 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
2747 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
2748 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
2750 [self loadRequest:request];
2754 - (void) loadURL:(NSURL *)url {
2755 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
2758 // XXX: this needs to add the headers
2759 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
2763 - (void) loadRequest:(NSURLRequest *)request {
2764 [webview_ loadRequest:request];
2767 - (void) reloadURL {
2768 NSURL *url = [[[urls_ lastObject] retain] autorelease];
2769 [urls_ removeLastObject];
2770 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
2773 - (WebView *) webView {
2774 return [webview_ webView];
2777 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
2778 [scroller_ setContentSize:frame.size];
2781 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
2782 [self view:sender didSetFrame:frame];
2785 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
2786 return [self _addHeadersToRequest:request];
2789 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
2790 if ([[[request URL] scheme] isEqualToString:@"apptapp"])
2792 [self setBackButtonTitle:title_];
2793 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2794 [browser setDelegate:delegate_];
2795 [book_ pushPage:browser];
2796 [browser loadRequest:[self _addHeadersToRequest:request]];
2797 return [browser webView];
2800 - (void) webView:(WebView *)sender willClickElement:(id)element {
2801 if (![element respondsToSelector:@selector(href)])
2803 NSString *href = [element href];
2806 if ([href hasPrefix:@"apptapp://package/"]) {
2807 NSString *name = [href substringFromIndex:18];
2808 Package *package = [database_ packageWithName:name];
2809 if (package == nil) {
2810 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2811 initWithTitle:@"Cannot Locate Package"
2812 buttons:[NSArray arrayWithObjects:@"Close", nil]
2813 defaultButtonIndex:0
2818 [sheet setBodyText:[NSString stringWithFormat:
2819 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
2822 [sheet popupAlertAnimated:YES];
2824 [self setBackButtonTitle:title_];
2825 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2826 [view setDelegate:delegate_];
2827 [view setPackage:package];
2828 [book_ pushPage:view];
2833 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
2834 title_ = [title retain];
2835 [self setTitle:title];
2838 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
2839 if ([frame parentFrame] != nil)
2844 [indicator_ startAnimation];
2845 [self reloadButtons];
2847 if (title_ != nil) {
2852 [self setTitle:@"Loading..."];
2854 WebView *webview = [webview_ webView];
2855 NSString *href = [webview mainFrameURL];
2856 [urls_ addObject:[NSURL URLWithString:href]];
2858 CGRect webrect = [scroller_ frame];
2859 webrect.size.height = 0;
2860 [webview_ setFrame:webrect];
2863 - (void) _finishLoading {
2866 [indicator_ stopAnimation];
2867 [self reloadButtons];
2871 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
2872 if ([frame parentFrame] != nil)
2874 [self _finishLoading];
2877 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
2878 if ([frame parentFrame] != nil)
2880 [self setTitle:[error localizedDescription]];
2881 [self _finishLoading];
2884 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2885 if ((self = [super initWithBook:book]) != nil) {
2886 database_ = database;
2889 struct CGRect bounds = [self bounds];
2891 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
2892 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
2893 [self addSubview:pinstripe];
2895 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
2896 [self addSubview:scroller_];
2898 [scroller_ setScrollingEnabled:YES];
2899 [scroller_ setAdjustForContentSizeChange:YES];
2900 [scroller_ setClipsSubviews:YES];
2901 [scroller_ setAllowsRubberBanding:YES];
2902 [scroller_ setScrollDecelerationFactor:0.99];
2903 [scroller_ setDelegate:self];
2905 CGRect webrect = [scroller_ bounds];
2906 webrect.size.height = 0;
2908 webview_ = [[UIWebView alloc] initWithFrame:webrect];
2909 [scroller_ addSubview:webview_];
2911 [webview_ setTilingEnabled:YES];
2912 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
2913 [webview_ setAutoresizes:YES];
2914 [webview_ setDelegate:self];
2915 //[webview_ setEnabledGestures:2];
2917 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
2918 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
2919 [indicator_ setStyle:0];
2921 Package *package([database_ packageWithName:@"cydia"]);
2922 NSString *application = package == nil ? @"Cydia" : [NSString
2923 stringWithFormat:@"Cydia/%@",
2927 WebView *webview = [webview_ webView];
2928 [webview setApplicationNameForUserAgent:application];
2929 [webview setFrameLoadDelegate:self];
2930 [webview setResourceLoadDelegate:self];
2931 [webview setUIDelegate:self];
2933 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
2937 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2941 - (void) _leftButtonClicked {
2942 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2943 initWithTitle:@"About Cydia Packager"
2944 buttons:[NSArray arrayWithObjects:@"Close", nil]
2945 defaultButtonIndex:0
2951 @"Copyright (C) 2008\n"
2952 "Jay Freeman (saurik)\n"
2953 "saurik@saurik.com\n"
2954 "http://www.saurik.com/\n"
2957 "http://www.theokorigroup.com/\n"
2959 "College of Creative Studies,\n"
2960 "University of California,\n"
2962 "http://www.ccs.ucsb.edu/"
2965 [sheet popupAlertAnimated:YES];
2968 - (void) _rightButtonClicked {
2973 - (NSString *) leftButtonTitle {
2977 - (NSString *) rightButtonTitle {
2978 return loading_ ? @"" : @"Reload";
2981 - (NSString *) title {
2985 - (NSString *) backButtonTitle {
2989 - (void) setPageActive:(BOOL)active {
2991 [book_ addSubview:indicator_];
2993 [indicator_ removeFromSuperview];
2996 - (void) resetViewAnimated:(BOOL)animated {
3002 @interface CYBook : RVBook <
3005 _transient Database *database_;
3007 UIProgressIndicator *indicator_;
3008 UITextLabel *prompt_;
3009 UIProgressBar *progress_;
3013 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3019 /* Install View {{{ */
3020 @interface InstallView : RVPage {
3021 _transient Database *database_;
3022 NSMutableArray *packages_;
3023 NSMutableArray *sections_;
3027 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3028 - (void) reloadData;
3032 @implementation InstallView
3035 [list_ setDataSource:nil];
3036 [list_ setDelegate:nil];
3038 [packages_ release];
3039 [sections_ release];
3044 - (int) numberOfRowsInTable:(UITable *)table {
3045 return [sections_ count] + 1;
3048 - (float) table:(UITable *)table heightForRow:(int)row {
3052 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3054 reusing = [[[SectionCell alloc] init] autorelease];
3055 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3059 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3063 - (void) tableRowSelected:(NSNotification *)notification {
3064 int row = [[notification object] selectedRow];
3073 title = @"All Packages";
3075 section = [sections_ objectAtIndex:(row - 1)];
3076 title = [section name];
3079 PackageTable *table = [[[PackageTable alloc]
3083 filter:@selector(isUninstalledInSection:)
3084 with:(section == nil ? nil : [section name])
3087 [table setDelegate:delegate_];
3089 [book_ pushPage:table];
3092 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3093 if ((self = [super initWithBook:book]) != nil) {
3094 database_ = database;
3096 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3097 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3099 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3100 [self addSubview:list_];
3102 UITableColumn *column = [[[UITableColumn alloc]
3103 initWithTitle:@"Name"
3105 width:[self frame].size.width
3108 [list_ setDataSource:self];
3109 [list_ setSeparatorStyle:1];
3110 [list_ addTableColumn:column];
3111 [list_ setDelegate:self];
3112 [list_ setReusesTableCells:YES];
3118 - (void) reloadData {
3119 NSArray *packages = [database_ packages];
3121 [packages_ removeAllObjects];
3122 [sections_ removeAllObjects];
3124 for (size_t i(0); i != [packages count]; ++i) {
3125 Package *package([packages objectAtIndex:i]);
3126 if ([package installed] == nil)
3127 [packages_ addObject:package];
3130 [packages_ sortUsingSelector:@selector(compareBySection:)];
3132 Section *section = nil;
3133 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3134 Package *package = [packages_ objectAtIndex:offset];
3135 NSString *name = [package section];
3137 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3138 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3139 [sections_ addObject:section];
3142 [section addToCount];
3148 - (void) resetViewAnimated:(BOOL)animated {
3149 [list_ resetViewAnimated:animated];
3152 - (NSString *) title {
3156 - (NSString *) backButtonTitle {
3162 /* Changes View {{{ */
3163 @interface ChangesView : RVPage {
3164 _transient Database *database_;
3165 NSMutableArray *packages_;
3166 NSMutableArray *sections_;
3167 UISectionList *list_;
3171 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3172 - (void) reloadData;
3176 @implementation ChangesView
3179 [[list_ table] setDelegate:nil];
3180 [list_ setDataSource:nil];
3182 [packages_ release];
3183 [sections_ release];
3188 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3189 return [sections_ count];
3192 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3193 return [[sections_ objectAtIndex:section] name];
3196 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3197 return [[sections_ objectAtIndex:section] row];
3200 - (int) numberOfRowsInTable:(UITable *)table {
3201 return [packages_ count];
3204 - (float) table:(UITable *)table heightForRow:(int)row {
3208 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3210 reusing = [[[PackageCell alloc] init] autorelease];
3211 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3215 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3219 - (void) tableRowSelected:(NSNotification *)notification {
3220 int row = [[notification object] selectedRow];
3223 Package *package = [packages_ objectAtIndex:row];
3224 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3225 [view setDelegate:delegate_];
3226 [view setPackage:package];
3227 [book_ pushPage:view];
3230 - (void) _leftButtonClicked {
3231 [(CYBook *)book_ update];
3232 [self reloadButtons];
3235 - (void) _rightButtonClicked {
3236 [delegate_ distUpgrade];
3239 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3240 if ((self = [super initWithBook:book]) != nil) {
3241 database_ = database;
3243 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3244 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3246 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3247 [self addSubview:list_];
3249 [list_ setShouldHideHeaderInShortLists:NO];
3250 [list_ setDataSource:self];
3251 //[list_ setSectionListStyle:1];
3253 UITableColumn *column = [[[UITableColumn alloc]
3254 initWithTitle:@"Name"
3256 width:[self frame].size.width
3259 UITable *table = [list_ table];
3260 [table setSeparatorStyle:1];
3261 [table addTableColumn:column];
3262 [table setDelegate:self];
3263 [table setReusesTableCells:YES];
3269 - (void) reloadData {
3270 NSArray *packages = [database_ packages];
3272 [packages_ removeAllObjects];
3273 [sections_ removeAllObjects];
3275 for (size_t i(0); i != [packages count]; ++i) {
3276 Package *package([packages objectAtIndex:i]);
3277 if ([package installed] == nil || [package upgradable])
3278 [packages_ addObject:package];
3281 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3283 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3284 Section *section = nil;
3287 bool unseens = false;
3289 CFLocaleRef locale = CFLocaleCopyCurrent();
3290 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3292 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3293 Package *package = [packages_ objectAtIndex:offset];
3295 if ([package upgradable]) {
3297 [upgradable addToCount];
3300 NSDate *seen = [package seen];
3305 name = [@"n/a ?" retain];
3307 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3310 if (section == nil || ![[section name] isEqualToString:name]) {
3311 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3312 [sections_ addObject:section];
3316 [section addToCount];
3320 CFRelease(formatter);
3324 Section *last = [sections_ lastObject];
3325 size_t count = [last count];
3326 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3327 [sections_ removeLastObject];
3331 [sections_ insertObject:upgradable atIndex:0];
3334 [self reloadButtons];
3337 - (void) resetViewAnimated:(BOOL)animated {
3338 [list_ resetViewAnimated:animated];
3341 - (NSString *) leftButtonTitle {
3342 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3345 - (NSString *) rightButtonTitle {
3346 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3349 - (NSString *) title {
3355 /* Manage View {{{ */
3356 @interface ManageView : PackageTable {
3359 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3363 @implementation ManageView
3365 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3370 filter:@selector(isInstalledInSection:)
3376 - (NSString *) title {
3377 return @"Installed Packages";
3380 - (NSString *) backButtonTitle {
3381 return @"All Packages";
3386 /* Search View {{{ */
3387 @protocol SearchViewDelegate
3388 - (void) showKeyboard:(BOOL)show;
3391 @interface SearchView : RVPage {
3393 UISearchField *field_;
3394 UITransitionView *transition_;
3395 PackageTable *table_;
3396 UIPreferencesTable *advanced_;
3401 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3402 - (void) reloadData;
3406 @implementation SearchView
3410 [[field_ textTraits] setEditingDelegate:nil];
3412 [field_ setDelegate:nil];
3414 [accessory_ release];
3416 [transition_ release];
3418 [advanced_ release];
3423 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3424 [delegate_ showKeyboard:YES];
3425 [table_ setEnabled:NO];
3426 [self addSubview:dimmed_];
3429 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3430 [dimmed_ removeFromSuperview];
3431 [table_ setEnabled:YES];
3432 [delegate_ showKeyboard:NO];
3435 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3436 NSString *text([field_ text]);
3437 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3440 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3441 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3445 [field_ resignFirstResponder];
3449 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3450 if ((self = [super initWithBook:book]) != nil) {
3451 CGRect pageBounds = [book_ pageBounds];
3453 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
3454 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3455 [self addSubview:pinstripe];*/
3457 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3458 CGColor dimmed(space_, 0, 0, 0, 0.5);
3459 [dimmed_ setBackgroundColor:dimmed];
3461 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3462 [self addSubview:transition_];
3464 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3466 table_ = [[PackageTable alloc]
3470 filter:@selector(isSearchedForBy:)
3474 [transition_ transition:0 toView:table_];
3476 CGRect cnfrect = {{1, 38}, {17, 18}};
3479 area.origin.x = cnfrect.size.width + 15;
3481 area.size.width = [self bounds].size.width - area.origin.x - 18;
3482 area.size.height = [UISearchField defaultHeight];
3484 field_ = [[UISearchField alloc] initWithFrame:area];
3486 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3487 [field_ setFont:font];
3490 [field_ setPlaceholder:@"Package Names & Descriptions"];
3491 [field_ setPaddingTop:5];
3492 [field_ setDelegate:self];
3495 UITextTraits *traits = [field_ textTraits];
3496 [traits setEditingDelegate:self];
3497 [traits setReturnKeyType:6];
3498 [traits setAutoCapsType:0];
3499 [traits setAutoCorrectionType:1];
3502 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3503 [configure setShowPressFeedback:YES];
3504 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
3505 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3507 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3508 [accessory_ addSubview:field_];
3509 [accessory_ addSubview:configure];
3514 LKAnimation *animation = [LKTransition animation];
3515 [animation setType:@"oglFlip"];
3516 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3517 [animation setFillMode:@"extended"];
3518 [animation setTransitionFlags:3];
3519 [animation setDuration:10];
3520 [animation setSpeed:0.35];
3521 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
3522 [[transition_ _layer] addAnimation:animation forKey:0];
3523 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3524 flipped_ = !flipped_;
3527 - (void) configurePushed {
3528 [field_ resignFirstResponder];
3532 - (void) resetViewAnimated:(BOOL)animated {
3535 [table_ resetViewAnimated:animated];
3538 - (void) reloadData {
3541 [table_ setObject:[field_ text]];
3542 [table_ reloadData];
3543 [table_ resetCursor];
3546 - (UIView *) accessoryView {
3550 - (NSString *) title {
3554 - (NSString *) backButtonTitle {
3558 - (void) setDelegate:(id)delegate {
3559 [table_ setDelegate:delegate];
3560 [super setDelegate:delegate];
3566 @implementation CYBook
3570 [indicator_ release];
3572 [progress_ release];
3576 - (NSString *) getTitleForPage:(RVPage *)page {
3577 return Simplify([super getTitleForPage:page]);
3585 [navbar_ setPrompt:@""];
3586 [navbar_ addSubview:overlay_];
3587 [indicator_ startAnimation];
3588 [prompt_ setText:@"Updating Database..."];
3589 [progress_ setProgress:0];
3594 detachNewThreadSelector:@selector(_update)
3603 [overlay_ removeFromSuperview];
3604 [indicator_ stopAnimation];
3605 [delegate_ reloadData];
3607 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3610 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3611 if ((self = [super initWithFrame:frame]) != nil) {
3612 database_ = database;
3614 CGRect ovrrect = [navbar_ bounds];
3615 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3617 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3619 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3620 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3621 CGRect indrect = {{indoffset, indoffset}, indsize};
3623 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3624 [indicator_ setStyle:2];
3625 [overlay_ addSubview:indicator_];
3627 CGSize prmsize = {200, indsize.width};
3630 indoffset * 2 + indsize.width,
3631 (ovrrect.size.height - prmsize.height) / 2
3634 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3636 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3638 [prompt_ setColor:White_];
3639 [prompt_ setBackgroundColor:Clear_];
3640 [prompt_ setFont:font];
3644 [overlay_ addSubview:prompt_];
3646 CGSize prgsize = {75, 100};
3649 ovrrect.size.width - prgsize.width - 10,
3650 (ovrrect.size.height - prgsize.height) / 2
3653 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3654 [progress_ setStyle:0];
3655 [overlay_ addSubview:progress_];
3660 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3663 status.setDelegate(self);
3665 [database_ updateWithStatus:status];
3668 performSelectorOnMainThread:@selector(_update_)
3676 - (void) setProgressError:(NSString *)error {
3678 performSelectorOnMainThread:@selector(_setProgressError:)
3684 - (void) setProgressTitle:(NSString *)title {
3686 performSelectorOnMainThread:@selector(_setProgressTitle:)
3692 - (void) setProgressPercent:(float)percent {
3695 - (void) addProgressOutput:(NSString *)output {
3697 performSelectorOnMainThread:@selector(_addProgressOutput:)
3703 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3707 - (void) _setProgressError:(NSString *)error {
3708 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
3711 - (void) _setProgressTitle:(NSString *)title {
3712 [prompt_ setText:[title stringByAppendingString:@"..."]];
3715 - (void) _addProgressOutput:(NSString *)output {
3720 @interface Cydia : UIApplication <
3721 ConfirmationViewDelegate,
3722 ProgressViewDelegate,
3731 UIButtonBar *buttonbar_;
3733 ConfirmationView *confirm_;
3735 Database *database_;
3736 ProgressView *progress_;
3740 UIKeyboard *keyboard_;
3745 @implementation Cydia
3747 - (void) _reloadData {
3748 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
3749 [hud setText:@"Reloading Data"];
3750 [overlay_ addSubview:hud];
3753 [database_ reloadData];
3757 if (Packages_ == nil) {
3758 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
3759 [Metadata_ setObject:Packages_ forKey:@"Packages"];
3764 NSArray *packages = [database_ packages];
3765 for (int i(0), e([packages count]); i != e; ++i) {
3766 Package *package = [packages objectAtIndex:i];
3767 if ([package upgradable])
3772 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
3773 [buttonbar_ setBadgeValue:badge forButton:3];
3774 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
3775 [buttonbar_ setBadgeAnimated:YES forButton:3];
3776 [self setApplicationBadge:badge];
3778 [buttonbar_ setBadgeValue:nil forButton:3];
3779 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
3780 [buttonbar_ setBadgeAnimated:NO forButton:3];
3781 [self removeApplicationBadge];
3784 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
3788 [hud removeFromSuperview];*/
3791 - (void) reloadData {
3792 @synchronized (self) {
3793 if (confirm_ == nil)
3799 pkgProblemResolver *resolver = [database_ resolver];
3801 resolver->InstallProtect();
3802 if (!resolver->Resolve(true))
3807 [database_ prepare];
3809 if ([database_ cache]->BrokenCount() == 0)
3810 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
3812 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
3813 NSArray *packages = [database_ packages];
3815 for (size_t i(0); i != [packages count]; ++i) {
3816 Package *package = [packages objectAtIndex:i];
3817 if ([package broken])
3818 [broken addObject:[package name]];
3821 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3822 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
3823 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3824 defaultButtonIndex:0
3829 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
3830 [sheet popupAlertAnimated:YES];
3836 - (void) installPackage:(Package *)package {
3837 @synchronized (self) {
3844 - (void) removePackage:(Package *)package {
3845 @synchronized (self) {
3852 - (void) distUpgrade {
3853 @synchronized (self) {
3854 [database_ upgrade];
3860 @synchronized (self) {
3868 [overlay_ removeFromSuperview];
3872 detachNewThreadSelector:@selector(perform)
3879 - (void) bootstrap_ {
3881 [database_ upgrade];
3882 [database_ prepare];
3883 [database_ perform];
3886 - (void) bootstrap {
3888 detachNewThreadSelector:@selector(bootstrap_)
3891 title:@"Bootstrap Install..."
3895 - (void) progressViewIsComplete:(ProgressView *)progress {
3896 @synchronized (self) {
3899 if (confirm_ != nil) {
3900 [underlay_ addSubview:overlay_];
3901 [confirm_ removeFromSuperview];
3908 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3912 - (void) setPage:(RVPage *)page {
3913 [page setDelegate:self];
3914 [book_ setPage:page];
3917 - (RVPage *) _setNewsPage {
3918 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3919 [self setPage:browser];
3920 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
3924 - (void) buttonBarItemTapped:(id)sender {
3925 unsigned tag = [sender tag];
3927 [book_ resetViewAnimated:YES];
3933 [self _setNewsPage];
3937 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
3941 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
3945 [self setPage:[[[ManageView alloc] initWithBook:book_ database:database_] autorelease]];
3949 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
3959 - (void) applicationWillSuspend {
3960 [super applicationWillSuspend];
3963 if (FW_LEAST(1,1,3))
3964 notify_post("com.apple.language.changed");
3966 system("launchctl stop com.apple.SpringBoard");
3969 - (void) applicationDidFinishLaunching:(id)unused {
3970 _assert(pkgInitConfig(*_config));
3971 _assert(pkgInitSystem(*_config, _system));
3976 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
3977 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
3979 [window_ orderFront: self];
3980 [window_ makeKey: self];
3981 [window_ _setHidden: NO];
3983 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
3984 [window_ setContentView:progress_];
3986 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
3987 [progress_ setContentView:underlay_];
3989 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
3992 [underlay_ addSubview:overlay_];
3994 database_ = [[Database alloc] init];
3995 [database_ setDelegate:progress_];
3997 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
3998 0, 0, screenrect.size.width, screenrect.size.height - 48
3999 ) database:database_];
4001 [book_ setDelegate:self];
4003 [overlay_ addSubview:book_];
4005 NSArray *buttonitems = [NSArray arrayWithObjects:
4006 [NSDictionary dictionaryWithObjectsAndKeys:
4007 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4008 @"news-up.png", kUIButtonBarButtonInfo,
4009 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4010 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4011 self, kUIButtonBarButtonTarget,
4012 @"News", kUIButtonBarButtonTitle,
4013 @"0", kUIButtonBarButtonType,
4016 [NSDictionary dictionaryWithObjectsAndKeys:
4017 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4018 @"install-up.png", kUIButtonBarButtonInfo,
4019 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4020 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4021 self, kUIButtonBarButtonTarget,
4022 @"Install", kUIButtonBarButtonTitle,
4023 @"0", kUIButtonBarButtonType,
4026 [NSDictionary dictionaryWithObjectsAndKeys:
4027 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4028 @"changes-up.png", kUIButtonBarButtonInfo,
4029 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4030 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4031 self, kUIButtonBarButtonTarget,
4032 @"Changes", kUIButtonBarButtonTitle,
4033 @"0", kUIButtonBarButtonType,
4036 [NSDictionary dictionaryWithObjectsAndKeys:
4037 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4038 @"manage-up.png", kUIButtonBarButtonInfo,
4039 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4040 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4041 self, kUIButtonBarButtonTarget,
4042 @"Manage", kUIButtonBarButtonTitle,
4043 @"0", kUIButtonBarButtonType,
4046 [NSDictionary dictionaryWithObjectsAndKeys:
4047 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4048 @"search-up.png", kUIButtonBarButtonInfo,
4049 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4050 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4051 self, kUIButtonBarButtonTarget,
4052 @"Search", kUIButtonBarButtonTitle,
4053 @"0", kUIButtonBarButtonType,
4057 buttonbar_ = [[UIButtonBar alloc]
4059 withFrame:CGRectMake(
4060 0, screenrect.size.height - 48,
4061 screenrect.size.width, 48
4063 withItemList:buttonitems
4066 [buttonbar_ setDelegate:self];
4067 [buttonbar_ setBarStyle:1];
4068 [buttonbar_ setButtonBarTrackingMode:2];
4070 int buttons[5] = {1, 2, 3, 4, 5};
4071 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4072 [buttonbar_ showButtonGroup:0 withDuration:0];
4074 for (int i = 0; i != 5; ++i)
4075 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4076 i * 64 + 2, 1, 60, 48
4079 [buttonbar_ showSelectionForButton:1];
4080 [overlay_ addSubview:buttonbar_];
4082 [UIKeyboard initImplementationNow];
4083 CGSize keysize = [UIKeyboard defaultSize];
4084 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4085 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4086 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4091 [progress_ resetView];
4096 [self _setNewsPage];
4099 - (void) showKeyboard:(BOOL)show {
4101 [overlay_ addSubview:keyboard_];
4103 [keyboard_ removeFromSuperview];
4106 - (void) slideUp:(UIAlertSheet *)alert {
4107 [alert presentSheetFromButtonBar:buttonbar_];
4112 void AddPreferences(NSString *plist) {
4113 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4115 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4116 _assert(settings != NULL);
4117 NSMutableArray *items = [settings objectForKey:@"items"];
4121 for (size_t i(0); i != [items count]; ++i) {
4122 NSMutableDictionary *item([items objectAtIndex:i]);
4123 NSString *label = [item objectForKey:@"label"];
4124 if (label != nil && [label isEqualToString:@"Cydia"]) {
4131 for (size_t i(0); i != [items count]; ++i) {
4132 NSDictionary *item([items objectAtIndex:i]);
4133 NSString *label = [item objectForKey:@"label"];
4134 if (label != nil && [label isEqualToString:@"General"]) {
4135 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4136 @"CydiaSettings", @"bundle",
4137 @"PSLinkCell", @"cell",
4138 [NSNumber numberWithBool:YES], @"hasIcon",
4139 [NSNumber numberWithBool:YES], @"isController",
4141 nil] atIndex:(i + 1)];
4147 _assert([settings writeToFile:plist atomically:YES] == YES);
4154 id Alloc_(id self, SEL selector) {
4155 id object = alloc_(self, selector);
4156 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4161 id Dealloc_(id self, SEL selector) {
4162 id object = dealloc_(self, selector);
4163 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4167 int main(int argc, char *argv[]) {
4168 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4170 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4172 Home_ = NSHomeDirectory();
4175 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4176 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4177 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4178 Sounds_Keyboard_ = [keyboard boolValue];
4184 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4185 alloc_ = alloc->method_imp;
4186 alloc->method_imp = (IMP) &Alloc_;*/
4188 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4189 dealloc_ = dealloc->method_imp;
4190 dealloc->method_imp = (IMP) &Dealloc_;*/
4192 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4193 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4194 Firmware_ = strdup([prover UTF8String]);
4195 NSArray *versions = [prover componentsSeparatedByString:@"."];
4196 int count = [versions count];
4197 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4198 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4199 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4204 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4205 char *machine = new char[size];
4206 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4209 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4210 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4211 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4212 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4216 IOObjectRelease(service);
4219 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4220 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4222 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4223 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4225 Packages_ = [Metadata_ objectForKey:@"Packages"];
4227 setenv("CYDIA", "", _not(int));
4228 if (access("/User", F_OK) != 0)
4229 system("/usr/libexec/cydia/firmware.sh");
4230 system("dpkg --configure -a");
4232 space_ = CGColorSpaceCreateDeviceRGB();
4234 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4235 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4236 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4237 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4239 int value = UIApplicationMain(argc, argv, [Cydia class]);
4241 CGColorSpaceRelease(space_);