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>
45 #include <WebKit/WebFrame.h>
46 #include <WebKit/WebView.h>
48 #include <objc/objc.h>
49 #include <objc/runtime.h>
55 #include <ext/stdio_filebuf.h>
57 #include <apt-pkg/acquire.h>
58 #include <apt-pkg/acquire-item.h>
59 #include <apt-pkg/algorithms.h>
60 #include <apt-pkg/cachefile.h>
61 #include <apt-pkg/configuration.h>
62 #include <apt-pkg/debmetaindex.h>
63 #include <apt-pkg/error.h>
64 #include <apt-pkg/init.h>
65 #include <apt-pkg/pkgrecords.h>
66 #include <apt-pkg/sourcelist.h>
67 #include <apt-pkg/sptr.h>
69 #include <sys/sysctl.h>
73 #include <mach-o/nlist.h>
83 /* Extension Keywords {{{ */
84 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
86 #define _assert(test) do \
88 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
93 #define _not(type) ((type) ~ (type) 0)
98 /* Miscellaneous Messages {{{ */
99 @interface NSString (Cydia)
100 - (NSString *) stringByAddingPercentEscapes;
101 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
104 /* External Constants {{{ */
105 extern NSString *kUIButtonBarButtonAction;
106 extern NSString *kUIButtonBarButtonInfo;
107 extern NSString *kUIButtonBarButtonInfoOffset;
108 extern NSString *kUIButtonBarButtonSelectedInfo;
109 extern NSString *kUIButtonBarButtonStyle;
110 extern NSString *kUIButtonBarButtonTag;
111 extern NSString *kUIButtonBarButtonTarget;
112 extern NSString *kUIButtonBarButtonTitle;
113 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
114 extern NSString *kUIButtonBarButtonTitleWidth;
115 extern NSString *kUIButtonBarButtonType;
122 #define $_ fprintf(stderr, "+");_trace();
123 #define _$ fprintf(stderr, "-");_trace();
126 /* iPhoneOS 2.0 Compatibility {{{ */
128 @interface UICGColor : NSObject {
131 - (id) initWithCGColor:(CGColorRef)color;
137 - (UIFont *) fontWithSize:(CGFloat)size;
140 @interface NSObject (iPhoneOS)
141 - (CGColorRef) cgColor;
142 - (CGColorRef) CGColor;
146 @implementation NSObject (iPhoneOS)
148 - (CGColorRef) cgColor {
149 return [self CGColor];
152 - (CGColorRef) CGColor {
153 return (CGColorRef) self;
157 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
162 @interface UITextView (iPhoneOS)
163 - (void) setTextSize:(float)size;
166 @implementation UITextView (iPhoneOS)
168 - (void) setTextSize:(float)size {
169 [self setFont:[[self font] fontWithSize:size]];
176 OBJC_EXPORT const char *class_getName(Class cls);
178 /* Reset View (UIView) {{{ */
179 @interface UIView (RVBook)
180 - (void) resetViewAnimated:(BOOL)animated;
184 @implementation UIView (RVBook)
186 - (void) resetViewAnimated:(BOOL)animated {
187 fprintf(stderr, "%s\n", class_getName(self->isa));
192 fprintf(stderr, "%s\n", class_getName(self->isa));
198 /* Reset View (UITable) {{{ */
199 @interface UITable (RVBook)
200 - (void) resetViewAnimated:(BOOL)animated;
204 @implementation UITable (RVBook)
206 - (void) resetViewAnimated:(BOOL)animated {
207 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
216 /* Reset View (UISectionList) {{{ */
217 @interface UISectionList (RVBook)
218 - (void) resetViewAnimated:(BOOL)animated;
222 @implementation UISectionList (RVBook)
224 - (void) resetViewAnimated:(BOOL)animated {
225 [[self table] resetViewAnimated:animated];
229 [[self table] clearView];
235 /* Perl-Compatible RegEx {{{ */
245 Pcre(const char *regex) :
250 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
253 fprintf(stderr, "%d:%s\n", offset, error);
257 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
258 matches_ = new int[(capture_ + 1) * 3];
266 NSString *operator [](size_t match) {
268 stringWithCString:(data_ + matches_[match * 2])
269 length:(matches_[match * 2 + 1] - matches_[match * 2])
273 bool operator ()(const char *data, size_t size) {
275 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
279 /* Mime Addresses {{{ */
280 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
282 @interface Address : NSObject {
288 - (NSString *) email;
290 + (Address *) addressWithString:(NSString *)string;
291 - (Address *) initWithString:(NSString *)string;
294 @implementation Address
303 - (NSString *) name {
307 - (NSString *) email {
311 + (Address *) addressWithString:(NSString *)string {
312 return [[[Address alloc] initWithString:string] autorelease];
315 - (Address *) initWithString:(NSString *)string {
316 if ((self = [super init]) != nil) {
317 const char *data = [string UTF8String];
318 size_t size = [string length];
320 if (email_r(data, size)) {
321 name_ = [email_r[1] retain];
322 email_ = [email_r[2] retain];
324 name_ = [[NSString stringWithCString:data length:size] retain];
332 /* CoreGraphics Primitives {{{ */
343 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
346 Set(space, red, green, blue, alpha);
351 CGColorRelease(color_);
358 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
360 float color[] = {red, green, blue, alpha};
361 color_ = CGColorCreate(space, color);
364 operator CGColorRef() {
379 /* Right Alignment {{{ */
380 @interface UIRightTextLabel : UITextLabel {
381 float _savedRightEdgeX;
382 BOOL _sizedtofit_flag;
385 - (void) setFrame:(CGRect)frame;
386 - (void) setText:(NSString *)text;
387 - (void) realignText;
390 @implementation UIRightTextLabel
392 - (void) setFrame:(CGRect)frame {
393 [super setFrame:frame];
394 if (_sizedtofit_flag == NO) {
395 _savedRightEdgeX = frame.origin.x;
400 - (void) setText:(NSString *)text {
401 [super setText:text];
405 - (void) realignText {
406 CGRect oldFrame = [self frame];
408 _sizedtofit_flag = YES;
409 [self sizeToFit]; // shrink down size so I can right align it
411 CGRect newFrame = [self frame];
413 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
414 oldFrame.size.width = newFrame.size.width;
415 [super setFrame:oldFrame];
416 _sizedtofit_flag = NO;
422 /* Random Global Variables {{{ */
423 static const int PulseInterval_ = 50000;
425 static CGColor Black_;
426 static CGColor Clear_;
428 static CGColor White_;
430 static NSString *Home_;
431 static BOOL Sounds_Keyboard_;
433 const char *Firmware_ = NULL;
434 const char *Machine_ = NULL;
435 const char *SerialNumber_ = NULL;
441 CGColorSpaceRef space_;
443 #define FW_LEAST(major, minor, bugfix) \
444 (major < Major_ || major == Major_ && \
445 (minor < Minor_ || minor == Minor_ && \
451 static NSMutableDictionary *Metadata_;
452 static NSMutableDictionary *Packages_;
455 NSString *GetLastUpdate() {
456 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
459 return @"Never or Unknown";
461 CFLocaleRef locale = CFLocaleCopyCurrent();
462 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
463 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
465 CFRelease(formatter);
468 return [(NSString *) formatted autorelease];
471 /* Display Helpers {{{ */
472 inline float Interpolate(float begin, float end, float fraction) {
473 return (end - begin) * fraction + begin;
476 NSString *SizeString(double size) {
478 while (size > 1024) {
483 static const char *powers_[] = {"B", "kB", "MB", "GB"};
485 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
488 static const float TextViewOffset_ = 22;
490 UITextView *GetTextView(NSString *value, float left, bool html) {
491 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
492 [text setEditable:NO];
493 [text setTextSize:16];
495 [text setHTML:value];
497 [text setText:value];
498 [text setEnabled:NO];
500 [text setBackgroundColor:Clear_];
502 CGRect frame = [text frame];
503 [text setFrame:frame];
504 CGRect rect = [text visibleTextRect];
505 frame.size.height = rect.size.height;
506 [text setFrame:frame];
511 NSString *Simplify(NSString *title) {
512 const char *data = [title UTF8String];
513 size_t size = [title length];
515 Pcre title_r("^(.*?)( \\(.*\\))?$");
516 if (title_r(data, size))
526 @protocol ProgressDelegate
527 - (void) setProgressError:(NSString *)error;
528 - (void) setProgressTitle:(NSString *)title;
529 - (void) setProgressPercent:(float)percent;
530 - (void) addProgressOutput:(NSString *)output;
533 @protocol CydiaDelegate
534 - (void) installPackage:(Package *)package;
535 - (void) removePackage:(Package *)package;
536 - (void) slideUp:(UIAlertSheet *)alert;
537 - (void) distUpgrade;
540 /* Status Delegation {{{ */
542 public pkgAcquireStatus
545 _transient id<ProgressDelegate> delegate_;
553 void setDelegate(id delegate) {
554 delegate_ = delegate;
557 virtual bool MediaChange(std::string media, std::string drive) {
561 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
564 virtual void Fetch(pkgAcquire::ItemDesc &item) {
565 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
568 virtual void Done(pkgAcquire::ItemDesc &item) {
571 virtual void Fail(pkgAcquire::ItemDesc &item) {
573 item.Owner->Status == pkgAcquire::Item::StatIdle ||
574 item.Owner->Status == pkgAcquire::Item::StatDone
578 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
581 virtual bool Pulse(pkgAcquire *Owner) {
582 bool value = pkgAcquireStatus::Pulse(Owner);
585 double(CurrentBytes + CurrentItems) /
586 double(TotalBytes + TotalItems)
589 [delegate_ setProgressPercent:percent];
593 virtual void Start() {
596 virtual void Stop() {
600 /* Progress Delegation {{{ */
605 _transient id<ProgressDelegate> delegate_;
608 virtual void Update() {
609 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
610 [delegate_ setProgressPercent:(Percent / 100)];
619 void setDelegate(id delegate) {
620 delegate_ = delegate;
623 virtual void Done() {
624 [delegate_ setProgressPercent:1];
629 /* Database Interface {{{ */
630 @interface Database : NSObject {
632 pkgRecords *records_;
633 pkgProblemResolver *resolver_;
634 pkgAcquire *fetcher_;
636 SPtr<pkgPackageManager> manager_;
637 pkgSourceList *list_;
639 NSMutableDictionary *sources_;
640 NSMutableArray *packages_;
642 _transient id delegate_;
648 - (void) _readStatus:(NSNumber *)fd;
649 - (void) _readOutput:(NSNumber *)fd;
651 - (Package *) packageWithName:(NSString *)name;
654 - (pkgCacheFile &) cache;
655 - (pkgRecords *) records;
656 - (pkgProblemResolver *) resolver;
657 - (pkgAcquire &) fetcher;
658 - (NSArray *) packages;
666 - (void) updateWithStatus:(Status &)status;
668 - (void) setDelegate:(id)delegate;
669 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
673 /* Source Class {{{ */
674 @interface Source : NSObject {
675 NSString *description_;
680 NSString *distribution_;
684 NSString *defaultIcon_;
689 - (Source *) initWithMetaIndex:(metaIndex *)index;
694 - (NSString *) distribution;
697 - (NSString *) description;
698 - (NSString *) label;
699 - (NSString *) origin;
700 - (NSString *) version;
702 - (NSString *) defaultIcon;
705 @implementation Source
709 [distribution_ release];
712 if (description_ != nil)
713 [description_ release];
720 if (defaultIcon_ != nil)
721 [defaultIcon_ release];
726 - (Source *) initWithMetaIndex:(metaIndex *)index {
727 if ((self = [super init]) != nil) {
728 trusted_ = index->IsTrusted();
730 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
731 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
732 type_ = [[NSString stringWithCString:index->GetType()] retain];
740 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
741 if (dindex != NULL) {
742 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
744 while (std::getline(release, line)) {
745 std::string::size_type colon(line.find(':'));
746 if (colon == std::string::npos)
749 std::string name(line.substr(0, colon));
750 std::string value(line.substr(colon + 1));
751 while (!value.empty() && value[0] == ' ')
752 value = value.substr(1);
754 if (name == "Default-Icon")
755 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
756 else if (name == "Description")
757 description_ = [[NSString stringWithCString:value.c_str()] retain];
758 else if (name == "Label")
759 label_ = [[NSString stringWithCString:value.c_str()] retain];
760 else if (name == "Origin")
761 origin_ = [[NSString stringWithCString:value.c_str()] retain];
762 else if (name == "Version")
763 version_ = [[NSString stringWithCString:value.c_str()] retain];
777 - (NSString *) distribution {
778 return distribution_;
781 - (NSString *) type {
785 - (NSString *) description {
789 - (NSString *) label {
793 - (NSString *) origin {
797 - (NSString *) version {
801 - (NSString *) defaultIcon {
807 /* Package Class {{{ */
808 NSString *Scour(const char *field, const char *begin, const char *end) {
809 size_t i(0), l(strlen(field));
812 const char *name = begin + i;
813 const char *colon = name + l;
814 const char *value = colon + 1;
819 memcmp(name, field, l) == 0
821 while (value != end && value[0] == ' ')
823 const char *line = std::find(value, end, '\n');
824 while (line != value && line[-1] == ' ')
826 return [NSString stringWithCString:value length:(line - value)];
828 begin = std::find(begin, end, '\n');
836 @interface Package : NSObject {
837 pkgCache::PkgIterator iterator_;
838 _transient Database *database_;
839 pkgCache::VerIterator version_;
840 pkgCache::VerFileIterator file_;
844 NSString *installed_;
853 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
854 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
856 - (NSString *) section;
857 - (Address *) maintainer;
859 - (NSString *) description;
860 - (NSString *) index;
864 - (NSString *) latest;
865 - (NSString *) installed;
872 - (NSString *) tagline;
874 - (NSString *) website;
878 - (BOOL) matches:(NSString *)text;
880 - (NSComparisonResult) compareByName:(Package *)package;
881 - (NSComparisonResult) compareBySection:(Package *)package;
882 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
883 - (NSComparisonResult) compareForChanges:(Package *)package;
888 - (NSNumber *) isSearchedForBy:(NSString *)search;
889 - (NSNumber *) isInstalledInSection:(NSString *)section;
890 - (NSNumber *) isUninstalledInSection:(NSString *)section;
894 @implementation Package
898 if (installed_ != nil)
899 [installed_ release];
915 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
916 if ((self = [super init]) != nil) {
917 iterator_ = iterator;
918 database_ = database;
923 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
925 const char *begin, *end;
926 parser->GetRec(begin, end);
928 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
929 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
931 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
932 name_ = Scour("Name", begin, end);
934 name_ = [name_ retain];
935 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
936 icon_ = Scour("Icon", begin, end);
938 icon_ = [icon_ retain];
939 website_ = Scour("Website", begin, end);
941 website_ = [website_ retain];
943 source_ = [[database_ getSource:file_.File()] retain];
945 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
946 if (metadata == nil || [metadata count] == 0) {
947 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
951 [Packages_ setObject:metadata forKey:id_];
956 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
957 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
958 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
959 return [[[Package alloc]
960 initWithIterator:iterator
968 - (NSString *) section {
969 const char *section = iterator_.Section();
970 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
973 - (Address *) maintainer {
974 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
975 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
979 return version_->InstalledSize;
982 - (NSString *) description {
983 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
984 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
986 NSArray *lines = [description componentsSeparatedByString:@"\n"];
987 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
988 if ([lines count] < 2)
991 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
992 for (size_t i(1); i != [lines count]; ++i) {
993 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
994 [trimmed addObject:trim];
997 return [trimmed componentsJoinedByString:@"\n"];
1000 - (NSString *) index {
1001 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1002 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1006 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1009 - (NSString *) latest {
1013 - (NSString *) installed {
1017 - (BOOL) upgradable {
1018 if (NSString *installed = [self installed])
1019 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1021 return [self essential];
1024 - (BOOL) essential {
1025 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1029 return (*[database_ cache])[iterator_].InstBroken();
1036 - (NSString *) name {
1037 return name_ == nil ? id_ : name_;
1040 - (NSString *) tagline {
1044 - (NSString *) icon {
1048 - (NSString *) website {
1052 - (Source *) source {
1056 - (BOOL) matches:(NSString *)text {
1062 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1063 if (range.location != NSNotFound)
1066 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1067 if (range.location != NSNotFound)
1070 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1071 if (range.location != NSNotFound)
1077 - (NSComparisonResult) compareByName:(Package *)package {
1078 NSString *lhs = [self name];
1079 NSString *rhs = [package name];
1081 if ([lhs length] != 0 && [rhs length] != 0) {
1082 unichar lhc = [lhs characterAtIndex:0];
1083 unichar rhc = [rhs characterAtIndex:0];
1085 if (isalpha(lhc) && !isalpha(rhc))
1086 return NSOrderedAscending;
1087 else if (!isalpha(lhc) && isalpha(rhc))
1088 return NSOrderedDescending;
1091 return [lhs caseInsensitiveCompare:rhs];
1094 - (NSComparisonResult) compareBySection:(Package *)package {
1095 NSString *lhs = [self section];
1096 NSString *rhs = [package section];
1098 if (lhs == NULL && rhs != NULL)
1099 return NSOrderedAscending;
1100 else if (lhs != NULL && rhs == NULL)
1101 return NSOrderedDescending;
1102 else if (lhs != NULL && rhs != NULL) {
1103 NSComparisonResult result = [lhs compare:rhs];
1104 if (result != NSOrderedSame)
1108 return NSOrderedSame;
1111 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1112 NSString *lhs = [self section];
1113 NSString *rhs = [package section];
1115 if (lhs == NULL && rhs != NULL)
1116 return NSOrderedAscending;
1117 else if (lhs != NULL && rhs == NULL)
1118 return NSOrderedDescending;
1119 else if (lhs != NULL && rhs != NULL) {
1120 NSComparisonResult result = [lhs compare:rhs];
1121 if (result != NSOrderedSame)
1125 return [self compareByName:package];
1128 - (NSComparisonResult) compareForChanges:(Package *)package {
1129 BOOL lhs = [self upgradable];
1130 BOOL rhs = [package upgradable];
1133 return lhs ? NSOrderedAscending : NSOrderedDescending;
1135 switch ([[self seen] compare:[package seen]]) {
1136 case NSOrderedAscending:
1137 return NSOrderedDescending;
1140 case NSOrderedDescending:
1141 return NSOrderedAscending;
1147 return [self compareByName:package];
1151 pkgProblemResolver *resolver = [database_ resolver];
1152 resolver->Clear(iterator_);
1153 resolver->Protect(iterator_);
1154 pkgCacheFile &cache([database_ cache]);
1155 cache->MarkInstall(iterator_, false);
1156 pkgDepCache::StateCache &state((*cache)[iterator_]);
1157 if (!state.Install())
1158 cache->SetReInstall(iterator_, true);
1162 pkgProblemResolver *resolver = [database_ resolver];
1163 resolver->Clear(iterator_);
1164 resolver->Protect(iterator_);
1165 resolver->Remove(iterator_);
1166 [database_ cache]->MarkDelete(iterator_, true);
1169 - (NSNumber *) isSearchedForBy:(NSString *)search {
1170 return [NSNumber numberWithBool:[self matches:search]];
1173 - (NSNumber *) isInstalledInSection:(NSString *)section {
1174 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1177 - (NSNumber *) isUninstalledInSection:(NSString *)section {
1178 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1183 /* Section Class {{{ */
1184 @interface Section : NSObject {
1190 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1191 - (NSString *) name;
1194 - (void) addToCount;
1198 @implementation Section
1205 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1206 if ((self = [super init]) != nil) {
1207 name_ = [name retain];
1212 - (NSString *) name {
1224 - (void) addToCount {
1231 /* Database Implementation {{{ */
1232 @implementation Database
1239 - (void) _readStatus:(NSNumber *)fd {
1240 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1242 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1243 std::istream is(&ib);
1248 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1250 pcre_extra *study = NULL;
1252 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1253 int matches[(capture + 1) * 3];
1255 while (std::getline(is, line)) {
1256 const char *data(line.c_str());
1258 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1260 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1263 [delegate_ setProgressPercent:(percent / 100)];
1265 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1266 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1268 if (type == "pmerror")
1269 [delegate_ setProgressError:string];
1270 else if (type == "pmstatus")
1271 [delegate_ setProgressTitle:string];
1272 else if (type == "pmconffile")
1274 else _assert(false);
1281 - (void) _readOutput:(NSNumber *)fd {
1282 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1284 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1285 std::istream is(&ib);
1288 while (std::getline(is, line))
1289 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1295 - (Package *) packageWithName:(NSString *)name {
1296 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1297 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1300 - (Database *) init {
1301 if ((self = [super init]) != nil) {
1307 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1308 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1312 _assert(pipe(fds) != -1);
1316 detachNewThreadSelector:@selector(_readStatus:)
1318 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1321 _assert(pipe(fds) != -1);
1322 _assert(dup2(fds[1], 1) != -1);
1323 _assert(close(fds[1]) != -1);
1326 detachNewThreadSelector:@selector(_readOutput:)
1328 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1333 - (pkgCacheFile &) cache {
1337 - (pkgRecords *) records {
1341 - (pkgProblemResolver *) resolver {
1345 - (pkgAcquire &) fetcher {
1349 - (NSArray *) packages {
1353 - (void) reloadData {
1363 if (!cache_.Open(progress_, true)) {
1364 fprintf(stderr, "repairing corrupted database...\n");
1366 [self updateWithStatus:status_];
1367 _assert(cache_.Open(progress_, true));
1370 now_ = [[NSDate date] retain];
1372 records_ = new pkgRecords(cache_);
1373 resolver_ = new pkgProblemResolver(cache_);
1374 fetcher_ = new pkgAcquire(&status_);
1377 list_ = new pkgSourceList();
1378 _assert(list_->ReadMainList());
1380 [sources_ removeAllObjects];
1381 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1382 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1383 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1385 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1386 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1390 [packages_ removeAllObjects];
1391 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1392 if (Package *package = [Package packageWithIterator:iterator database:self])
1393 if ([package source] != nil || [package installed] != nil)
1394 [packages_ addObject:package];
1398 pkgRecords records(cache_);
1400 lock_ = new FileFd();
1401 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1402 _assert(!_error->PendingError());
1405 // XXX: explain this with an error message
1406 _assert(list.ReadMainList());
1408 manager_ = (_system->CreatePM(cache_));
1409 _assert(manager_->GetArchives(fetcher_, &list, &records));
1410 _assert(!_error->PendingError());
1415 _assert(list.ReadMainList());
1417 /*std::map<std::string> before;
1419 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source)
1420 before.add((*source)->GetURI().c_str());
1423 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1427 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1429 if (result == pkgPackageManager::Failed)
1431 if (_error->PendingError())
1433 if (result != pkgPackageManager::Completed)
1436 _assert(list.ReadMainList());
1440 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1441 _assert(pkgApplyStatus(cache_));
1443 if (cache_->BrokenCount() != 0) {
1444 _assert(pkgFixBroken(cache_));
1445 _assert(cache_->BrokenCount() == 0);
1446 _assert(pkgMinimizeUpgrade(cache_));
1449 _assert(pkgDistUpgrade(cache_));
1453 [self updateWithStatus:status_];
1456 - (void) updateWithStatus:(Status &)status {
1458 _assert(list.ReadMainList());
1461 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1462 _assert(!_error->PendingError());
1464 pkgAcquire fetcher(&status);
1465 _assert(list.GetIndexes(&fetcher));
1467 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1468 bool failed = false;
1469 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1470 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1471 (*item)->Finished();
1475 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1476 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1477 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1480 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1484 - (void) setDelegate:(id)delegate {
1485 delegate_ = delegate;
1486 status_.setDelegate(delegate);
1487 progress_.setDelegate(delegate);
1490 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1491 pkgIndexFile *index(NULL);
1492 list_->FindIndex(file, index);
1493 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1499 /* RVPage Interface {{{ */
1502 @interface RVPage : UIView {
1503 _transient RVBook *book_;
1504 _transient id delegate_;
1507 - (NSString *) title;
1508 - (NSString *) backButtonTitle;
1509 - (NSString *) rightButtonTitle;
1510 - (NSString *) leftButtonTitle;
1511 - (UIView *) accessoryView;
1513 - (void) _rightButtonClicked;
1514 - (void) _leftButtonClicked;
1516 - (void) setPageActive:(BOOL)active;
1517 - (void) resetViewAnimated:(BOOL)animated;
1519 - (void) setTitle:(NSString *)title;
1520 - (void) setBackButtonTitle:(NSString *)title;
1522 - (void) reloadButtons;
1523 - (void) reloadData;
1525 - (id) initWithBook:(RVBook *)book;
1527 - (void) setDelegate:(id)delegate;
1531 /* Reset View {{{ */
1532 @protocol RVDelegate
1533 - (void) setPageActive:(BOOL)active with:(id)object;
1534 - (void) resetViewAnimated:(BOOL)animated with:(id)object;
1535 - (void) reloadDataWith:(id)object;
1538 @interface RVBook : UIView {
1539 NSMutableArray *pages_;
1540 UINavigationBar *navbar_;
1541 UITransitionView *transition_;
1543 _transient id delegate_;
1546 - (id) initWithFrame:(CGRect)frame;
1547 - (void) setDelegate:(id)delegate;
1549 - (void) setPage:(RVPage *)page;
1551 - (void) pushPage:(RVPage *)page;
1552 - (void) popPages:(unsigned)pages;
1554 - (void) setPrompt:(NSString *)prompt;
1556 - (void) resetViewAnimated:(BOOL)animated;
1557 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
1559 - (void) setTitle:(NSString *)title forPage:(RVPage *)page;
1560 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
1561 - (void) reloadButtonsForPage:(RVPage *)page;
1563 - (void) reloadData;
1565 - (CGRect) pageBounds;
1569 @implementation RVBook
1572 [navbar_ setDelegate:nil];
1576 [transition_ release];
1580 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1581 _assert([pages_ count] != 0);
1582 RVPage *page = [pages_ lastObject];
1584 case 0: [page _rightButtonClicked]; break;
1585 case 1: [page _leftButtonClicked]; break;
1589 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
1590 _assert([pages_ count] != 0);
1592 [[pages_ lastObject] setPageActive:NO];
1593 [pages_ removeLastObject];
1595 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1598 - (id) initWithFrame:(CGRect)frame {
1599 if ((self = [super initWithFrame:frame]) != nil) {
1600 pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
1602 struct CGRect bounds = [self bounds];
1603 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
1604 CGRect navrect = {{0, 0}, navsize};
1606 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1607 [self addSubview:navbar_];
1609 [navbar_ setBarStyle:1];
1610 [navbar_ setDelegate:self];
1612 [navbar_ setPrompt:@""];
1614 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
1615 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
1618 [self addSubview:transition_];
1622 - (void) setDelegate:(id)delegate {
1623 delegate_ = delegate;
1626 - (void) setPage:(RVPage *)page {
1627 if ([pages_ count] != 0)
1628 [[pages_ lastObject] setPageActive:NO];
1630 [navbar_ disableAnimation];
1632 for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
1633 [navbar_ popNavigationItem];
1636 [self pushPage:page];
1637 [navbar_ enableAnimation];
1640 - (void) pushPage:(RVPage *)page {
1641 if ([pages_ count] != 0)
1642 [[pages_ lastObject] setPageActive:NO];
1644 NSString *title = Simplify([page title]);
1646 NSString *backButtonTitle = [page backButtonTitle];
1647 if (backButtonTitle == nil)
1648 backButtonTitle = title;
1650 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1651 [navitem setBackButtonTitle:backButtonTitle];
1652 [navbar_ pushNavigationItem:navitem];
1654 BOOL animated = [pages_ count] == 0 ? NO : YES;
1655 [transition_ transition:(animated ? 1 : 0) toView:page];
1656 [page setPageActive:YES];
1658 [pages_ addObject:page];
1659 [self reloadButtonsForPage:page];
1661 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
1664 - (void) popPages:(unsigned)pages {
1668 [[pages_ lastObject] setPageActive:NO];
1671 for (unsigned i(0); i != pages; ++i)
1672 [navbar_ popNavigationItem];
1675 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1678 - (void) setPrompt:(NSString *)prompt {
1679 [navbar_ setPrompt:prompt];
1682 - (void) resetViewAnimated:(BOOL)animated {
1685 if ([pages_ count] > 1) {
1686 [navbar_ disableAnimation];
1687 while ([pages_ count] != (animated ? 2 : 1))
1688 [navbar_ popNavigationItem];
1689 [navbar_ enableAnimation];
1691 [navbar_ popNavigationItem];
1696 [self resetViewAnimated:animated toPage:[pages_ lastObject]];
1699 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
1700 [page resetViewAnimated:animated];
1701 [transition_ transition:(animated ? 2 : 0) toView:page];
1702 [page setPageActive:YES];
1703 [self reloadButtonsForPage:page];
1704 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
1707 - (void) setTitle:(NSString *)title forPage:(RVPage *)page {
1708 if ([pages_ count] == 0 || page != [pages_ lastObject])
1710 UINavigationItem *navitem = [navbar_ topItem];
1711 [navitem setTitle:title];
1714 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
1715 if ([pages_ count] == 0 || page != [pages_ lastObject])
1717 UINavigationItem *navitem = [navbar_ topItem];
1718 [navitem setBackButtonTitle:title];
1721 - (void) reloadButtonsForPage:(RVPage *)page {
1722 if ([pages_ count] == 0 || page != [pages_ lastObject])
1724 NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
1725 [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
1728 - (void) reloadData {
1729 for (int i(0), e([pages_ count]); i != e; ++i) {
1730 RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
1735 - (CGRect) pageBounds {
1736 return [transition_ bounds];
1741 /* RVPage Implementation {{{ */
1742 @implementation RVPage
1744 - (NSString *) title {
1745 [self doesNotRecognizeSelector:_cmd];
1749 - (NSString *) backButtonTitle {
1753 - (NSString *) leftButtonTitle {
1757 - (NSString *) rightButtonTitle {
1761 - (void) _rightButtonClicked {
1762 [self doesNotRecognizeSelector:_cmd];
1765 - (void) _leftButtonClicked {
1766 [self doesNotRecognizeSelector:_cmd];
1769 - (UIView *) accessoryView {
1773 - (void) setPageActive:(BOOL)active {
1776 - (void) resetViewAnimated:(BOOL)animated {
1777 [self doesNotRecognizeSelector:_cmd];
1780 - (void) setTitle:(NSString *)title {
1781 [book_ setTitle:title forPage:self];
1784 - (void) setBackButtonTitle:(NSString *)title {
1785 [book_ setBackButtonTitle:title forPage:self];
1788 - (void) reloadButtons {
1789 [book_ reloadButtonsForPage:self];
1792 - (void) reloadData {
1795 - (id) initWithBook:(RVBook *)book {
1796 if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
1801 - (void) setDelegate:(id)delegate {
1802 delegate_ = delegate;
1808 /* Confirmation View {{{ */
1809 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1810 if ([packages count] == 0)
1813 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1814 [fields setObject:text forKey:key];
1816 CGColor blue(space_, 0, 0, 0.4, 1);
1817 [text setTextColor:blue];
1820 @protocol ConfirmationViewDelegate
1825 @interface ConfirmationView : UIView {
1826 Database *database_;
1828 UITransitionView *transition_;
1830 UINavigationBar *navbar_;
1831 UIPreferencesTable *table_;
1832 NSMutableDictionary *fields_;
1833 UIAlertSheet *essential_;
1838 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1842 @implementation ConfirmationView
1845 [navbar_ setDelegate:nil];
1846 [transition_ setDelegate:nil];
1847 [table_ setDataSource:nil];
1849 [transition_ release];
1854 if (essential_ != nil)
1855 [essential_ release];
1860 [transition_ transition:7 toView:nil];
1864 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1865 if (from != nil && to == nil)
1866 [self removeFromSuperview];
1869 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1872 if (essential_ != nil)
1873 [essential_ popupAlertAnimated:YES];
1875 [delegate_ confirm];
1884 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1885 [essential_ dismiss];
1889 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1893 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1895 case 0: return @"Statistics";
1896 case 1: return @"Modifications";
1898 default: _assert(false);
1902 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1905 case 1: return [fields_ count];
1907 default: _assert(false);
1911 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1912 if (group != 1 || row == -1)
1915 _assert(size_t(row) < [fields_ count]);
1916 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1920 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1921 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1922 [cell setShowSelection:NO];
1925 case 0: switch (row) {
1927 [cell setTitle:@"Downloading"];
1928 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1932 [cell setTitle:@"Resuming At"];
1933 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1937 double size([database_ cache]->UsrSize());
1940 [cell setTitle:@"Disk Freeing"];
1941 [cell setValue:SizeString(-size)];
1943 [cell setTitle:@"Disk Using"];
1944 [cell setValue:SizeString(size)];
1948 default: _assert(false);
1952 _assert(size_t(row) < [fields_ count]);
1953 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1954 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1957 default: _assert(false);
1963 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1964 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1965 database_ = database;
1966 delegate_ = delegate;
1968 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1969 [self addSubview:transition_];
1971 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1973 CGSize navsize = [UINavigationBar defaultSize];
1974 CGRect navrect = {{0, 0}, navsize};
1975 CGRect bounds = [overlay_ bounds];
1977 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1978 [navbar_ setBarStyle:1];
1979 [navbar_ setDelegate:self];
1981 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1982 [navbar_ pushNavigationItem:navitem];
1983 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1985 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1987 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1988 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1989 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1990 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1991 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
1995 pkgCacheFile &cache([database_ cache]);
1996 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
1997 Package *package([Package packageWithIterator:iterator database:database_]);
1998 NSString *name([package name]);
1999 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
2000 pkgDepCache::StateCache &state(cache[iterator]);
2002 if (state.NewInstall())
2003 [installing addObject:name];
2004 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2005 [reinstalling addObject:name];
2006 else if (state.Upgrade())
2007 [upgrading addObject:name];
2008 else if (state.Downgrade())
2009 [downgrading addObject:name];
2010 else if (state.Delete()) {
2013 [removing addObject:name];
2020 essential_ = [[UIAlertSheet alloc]
2021 initWithTitle:@"Unable to Comply"
2022 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2023 defaultButtonIndex:0
2028 [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."];
2031 AddTextView(fields_, installing, @"Installing");
2032 AddTextView(fields_, reinstalling, @"Reinstalling");
2033 AddTextView(fields_, upgrading, @"Upgrading");
2034 AddTextView(fields_, downgrading, @"Downgrading");
2035 AddTextView(fields_, removing, @"Removing");
2037 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2038 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2041 [table_ setReusesTableCells:YES];
2042 [table_ setDataSource:self];
2043 [table_ reloadData];
2045 [overlay_ addSubview:navbar_];
2046 [overlay_ addSubview:table_];
2048 [view addSubview:self];
2050 [transition_ setDelegate:self];
2052 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2053 [transition_ transition:0 toView:blank];
2054 [transition_ transition:3 toView:overlay_];
2061 /* Progress Data {{{ */
2062 @interface ProgressData : NSObject {
2068 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2075 @implementation ProgressData
2077 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2078 if ((self = [super init]) != nil) {
2079 selector_ = selector;
2099 /* Progress View {{{ */
2100 @interface ProgressView : UIView <
2104 UIView *background_;
2105 UITransitionView *transition_;
2107 UINavigationBar *navbar_;
2108 UIProgressBar *progress_;
2109 UITextView *output_;
2110 UITextLabel *status_;
2114 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2116 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
2117 - (void) setContentView:(UIView *)view;
2120 - (void) _retachThread;
2121 - (void) _detachNewThreadData:(ProgressData *)data;
2122 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2126 @protocol ProgressViewDelegate
2127 - (void) progressViewIsComplete:(ProgressView *)sender;
2130 @implementation ProgressView
2133 [transition_ setDelegate:nil];
2134 [navbar_ setDelegate:nil];
2137 if (background_ != nil)
2138 [background_ release];
2139 [transition_ release];
2142 [progress_ release];
2148 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2149 if (bootstrap_ && from == overlay_ && to == view_)
2153 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
2154 if ((self = [super initWithFrame:frame]) != nil) {
2155 delegate_ = delegate;
2157 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2158 [transition_ setDelegate:self];
2160 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2163 [overlay_ setBackgroundColor:Black_];
2165 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2166 [background_ setBackgroundColor:Black_];
2167 [self addSubview:background_];
2170 [self addSubview:transition_];
2172 CGSize navsize = [UINavigationBar defaultSize];
2173 CGRect navrect = {{0, 0}, navsize};
2175 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2176 [overlay_ addSubview:navbar_];
2178 [navbar_ setBarStyle:1];
2179 [navbar_ setDelegate:self];
2181 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2182 [navbar_ pushNavigationItem:navitem];
2184 CGRect bounds = [overlay_ bounds];
2185 CGSize prgsize = [UIProgressBar defaultSize];
2188 (bounds.size.width - prgsize.width) / 2,
2189 bounds.size.height - prgsize.height - 20
2192 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2193 [overlay_ addSubview:progress_];
2195 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2197 bounds.size.height - prgsize.height - 50,
2198 bounds.size.width - 20,
2202 [status_ setColor:White_];
2203 [status_ setBackgroundColor:Clear_];
2205 [status_ setCentersHorizontally:YES];
2206 //[status_ setFont:font];
2208 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2210 navrect.size.height + 20,
2211 bounds.size.width - 20,
2212 bounds.size.height - navsize.height - 62 - navrect.size.height
2215 //[output_ setTextFont:@"Courier New"];
2216 [output_ setTextSize:12];
2218 [output_ setTextColor:White_];
2219 [output_ setBackgroundColor:Clear_];
2221 [output_ setMarginTop:0];
2222 [output_ setAllowsRubberBanding:YES];
2224 [overlay_ addSubview:output_];
2225 [overlay_ addSubview:status_];
2227 [progress_ setStyle:0];
2231 - (void) setContentView:(UIView *)view {
2232 view_ = [view retain];
2235 - (void) resetView {
2236 [transition_ transition:6 toView:view_];
2239 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2243 - (void) _retachThread {
2244 [delegate_ progressViewIsComplete:self];
2248 - (void) _detachNewThreadData:(ProgressData *)data {
2249 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2251 [[data target] performSelector:[data selector] withObject:[data object]];
2254 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2259 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2260 [navbar_ popNavigationItem];
2261 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
2262 [navbar_ pushNavigationItem:navitem];
2264 [status_ setText:nil];
2265 [output_ setText:@""];
2266 [progress_ setProgress:0];
2268 [transition_ transition:6 toView:overlay_];
2271 detachNewThreadSelector:@selector(_detachNewThreadData:)
2273 withObject:[[ProgressData alloc]
2274 initWithSelector:selector
2281 - (void) setProgressError:(NSString *)error {
2283 performSelectorOnMainThread:@selector(_setProgressError:)
2289 - (void) setProgressTitle:(NSString *)title {
2291 performSelectorOnMainThread:@selector(_setProgressTitle:)
2297 - (void) setProgressPercent:(float)percent {
2299 performSelectorOnMainThread:@selector(_setProgressPercent:)
2300 withObject:[NSNumber numberWithFloat:percent]
2305 - (void) addProgressOutput:(NSString *)output {
2307 performSelectorOnMainThread:@selector(_addProgressOutput:)
2313 - (void) _setProgressError:(NSString *)error {
2314 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2315 initWithTitle:@"Package Error"
2316 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2317 defaultButtonIndex:0
2322 [sheet setBodyText:error];
2323 [sheet popupAlertAnimated:YES];
2326 - (void) _setProgressTitle:(NSString *)title {
2327 [status_ setText:[title stringByAppendingString:@"..."]];
2330 - (void) _setProgressPercent:(NSNumber *)percent {
2331 [progress_ setProgress:[percent floatValue]];
2334 - (void) _addProgressOutput:(NSString *)output {
2335 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2336 CGSize size = [output_ contentSize];
2337 CGRect rect = {{0, size.height}, {size.width, 0}};
2338 [output_ scrollRectToVisible:rect animated:YES];
2344 /* Package Cell {{{ */
2345 @interface PackageCell : UITableCell {
2348 UITextLabel *description_;
2349 UITextLabel *source_;
2350 UIImageView *trusted_;
2353 - (PackageCell *) init;
2354 - (void) setPackage:(Package *)package;
2356 - (void) _setSelected:(float)fraction;
2357 - (void) setSelected:(BOOL)selected;
2358 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2359 - (void) _setSelectionFadeFraction:(float)fraction;
2363 @implementation PackageCell
2368 [description_ release];
2374 - (PackageCell *) init {
2375 if ((self = [super init]) != nil) {
2376 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2377 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2378 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2380 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2382 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2383 [name_ setBackgroundColor:Clear_];
2384 [name_ setFont:bold];
2386 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2387 [source_ setBackgroundColor:Clear_];
2388 [source_ setFont:large];
2390 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2391 [description_ setBackgroundColor:Clear_];
2392 [description_ setFont:small];
2394 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2395 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
2397 [self addSubview:icon_];
2398 [self addSubview:name_];
2399 [self addSubview:description_];
2400 [self addSubview:source_];
2408 - (void) setPackage:(Package *)package {
2409 Source *source = [package source];
2411 UIImage *image = nil;
2412 if (NSString *icon = [package icon])
2413 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2414 if (image == nil) if (NSString *icon = [source defaultIcon])
2415 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2417 image = [UIImage applicationImageNamed:@"unknown.png"];
2418 [icon_ setImage:image];
2421 CGSize size = [image size];
2422 float scale = 30 / std::max(size.width, size.height);
2423 [icon_ zoomToScale:scale];
2426 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2428 [name_ setText:[package name]];
2429 [description_ setText:[package tagline]];
2434 if (source != nil) {
2435 label = [source label];
2436 trusted = [source trusted];
2437 } else if ([[package id] isEqualToString:@"firmware"]) {
2441 label = @"Unknown/Local";
2445 [source_ setText:[NSString stringWithFormat:@"from %@ (%@)", label, Simplify([package section])]];
2448 [self addSubview:trusted_];
2450 [trusted_ removeFromSuperview];
2453 - (void) _setSelected:(float)fraction {
2454 CGColor black(space_,
2455 Interpolate(0.0, 1.0, fraction),
2456 Interpolate(0.0, 1.0, fraction),
2457 Interpolate(0.0, 1.0, fraction),
2460 CGColor gray(space_,
2461 Interpolate(0.4, 1.0, fraction),
2462 Interpolate(0.4, 1.0, fraction),
2463 Interpolate(0.4, 1.0, fraction),
2466 [name_ setColor:black];
2467 [description_ setColor:gray];
2468 [source_ setColor:black];
2471 - (void) setSelected:(BOOL)selected {
2472 [self _setSelected:(selected ? 1.0 : 0.0)];
2473 [super setSelected:selected];
2476 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2478 [self _setSelected:(selected ? 1.0 : 0.0)];
2479 [super setSelected:selected withFade:fade];
2482 - (void) _setSelectionFadeFraction:(float)fraction {
2483 [self _setSelected:fraction];
2484 [super _setSelectionFadeFraction:fraction];
2489 /* Section Cell {{{ */
2490 @interface SectionCell : UITableCell {
2492 UITextLabel *count_;
2496 - (void) setSection:(Section *)section;
2498 - (void) _setSelected:(float)fraction;
2499 - (void) setSelected:(BOOL)selected;
2500 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2501 - (void) _setSelectionFadeFraction:(float)fraction;
2505 @implementation SectionCell
2514 if ((self = [super init]) != nil) {
2515 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2516 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2518 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2519 [name_ setBackgroundColor:Clear_];
2520 [name_ setFont:bold];
2522 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2523 [count_ setCentersHorizontally:YES];
2524 [count_ setBackgroundColor:Clear_];
2525 [count_ setFont:small];
2526 [count_ setColor:White_];
2528 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2529 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2531 [self addSubview:folder];
2532 [self addSubview:name_];
2533 [self addSubview:count_];
2535 [self _setSelected:0];
2542 - (void) setSection:(Section *)section {
2543 if (section == nil) {
2544 [name_ setText:@"All Packages"];
2545 [count_ setText:nil];
2547 NSString *name = [section name];
2548 [name_ setText:(name == nil ? @"(No Section)" : name)];
2549 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2553 - (void) _setSelected:(float)fraction {
2554 CGColor black(space_,
2555 Interpolate(0.0, 1.0, fraction),
2556 Interpolate(0.0, 1.0, fraction),
2557 Interpolate(0.0, 1.0, fraction),
2560 [name_ setColor:black];
2563 - (void) setSelected:(BOOL)selected {
2564 [self _setSelected:(selected ? 1.0 : 0.0)];
2565 [super setSelected:selected];
2568 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2570 [self _setSelected:(selected ? 1.0 : 0.0)];
2571 [super setSelected:selected withFade:fade];
2574 - (void) _setSelectionFadeFraction:(float)fraction {
2575 [self _setSelected:fraction];
2576 [super _setSelectionFadeFraction:fraction];
2582 /* Browser Interface {{{ */
2583 @interface BrowserView : RVPage {
2584 _transient Database *database_;
2585 UIScroller *scroller_;
2586 UIWebView *webview_;
2587 NSMutableArray *urls_;
2588 UIProgressIndicator *indicator_;
2594 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2595 - (void) loadURL:(NSURL *)url;
2597 - (void) loadRequest:(NSURLRequest *)request;
2600 - (WebView *) webView;
2602 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2607 /* Package View {{{ */
2608 @protocol PackageViewDelegate
2609 - (void) performPackage:(Package *)package;
2612 @interface PackageView : RVPage {
2613 _transient Database *database_;
2614 UIPreferencesTable *table_;
2617 UITextView *description_;
2620 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2621 - (void) setPackage:(Package *)package;
2625 @implementation PackageView
2628 [table_ setDataSource:nil];
2629 [table_ setDelegate:nil];
2631 if (package_ != nil)
2635 if (description_ != nil)
2636 [description_ release];
2641 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2642 return [package_ source] == nil ? 2 : 3;
2645 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2648 case 1: return @"Package Details";
2649 case 2: return @"Source Information";
2651 default: _assert(false);
2655 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2656 if (group != 0 || row != 1)
2659 return [description_ visibleTextRect].size.height + TextViewOffset_;
2662 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2664 case 0: return [package_ website] == nil ? 2 : 3;
2668 default: _assert(false);
2672 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2673 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2674 [cell setShowSelection:NO];
2677 case 0: switch (row) {
2679 [cell setTitle:[package_ name]];
2680 [cell setValue:[package_ latest]];
2684 [cell addSubview:description_];
2688 [cell setTitle:@"More Information"];
2689 [cell setShowDisclosure:YES];
2690 [cell setShowSelection:YES];
2693 default: _assert(false);
2696 case 1: switch (row) {
2698 [cell setTitle:@"Identifier"];
2699 [cell setValue:[package_ id]];
2703 [cell setTitle:@"Installed Version"];
2704 NSString *installed([package_ installed]);
2705 [cell setValue:(installed == nil ? @"n/a" : installed)];
2709 [cell setTitle:@"Section"];
2710 NSString *section([package_ section]);
2711 [cell setValue:(section == nil ? @"n/a" : section)];
2715 [cell setTitle:@"Expanded Size"];
2716 [cell setValue:SizeString([package_ size])];
2720 [cell setTitle:@"Maintainer"];
2721 [cell setValue:[[package_ maintainer] name]];
2722 [cell setShowDisclosure:YES];
2723 [cell setShowSelection:YES];
2726 default: _assert(false);
2729 case 2: switch (row) {
2731 [cell setTitle:[[package_ source] label]];
2732 [cell setValue:[[package_ source] version]];
2736 [cell setValue:[[package_ source] description]];
2740 [cell setTitle:@"Origin"];
2741 [cell setValue:[[package_ source] origin]];
2744 default: _assert(false);
2747 default: _assert(false);
2753 - (BOOL) canSelectRow:(int)row {
2757 - (void) tableRowSelected:(NSNotification *)notification {
2758 int row = [table_ selectedRow];
2759 NSString *website = [package_ website];
2761 if (row == (website == nil ? 8 : 9))
2762 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2763 [[package_ maintainer] email],
2764 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2766 else if (website != nil && row == 3) {
2767 NSURL *url = [NSURL URLWithString:website];
2768 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2769 [browser setDelegate:delegate_];
2770 [book_ pushPage:browser];
2771 [browser loadURL:url];
2775 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2777 case 1: [delegate_ installPackage:package_]; break;
2778 case 2: [delegate_ removePackage:package_]; break;
2784 - (void) _rightButtonClicked {
2785 if ([package_ installed] == nil)
2786 [delegate_ installPackage:package_];
2788 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2790 if ([package_ upgradable])
2791 [buttons addObject:@"Upgrade"];
2793 [buttons addObject:@"Reinstall"];
2795 [buttons addObject:@"Remove"];
2796 [buttons addObject:@"Cancel"];
2798 [delegate_ slideUp:[[[UIAlertSheet alloc]
2799 initWithTitle:@"Manage Package"
2801 defaultButtonIndex:2
2808 - (NSString *) rightButtonTitle {
2809 _assert(package_ != nil);
2810 return [package_ installed] == nil ? @"Install" : @"Manage";
2813 - (NSString *) title {
2817 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2818 if ((self = [super initWithBook:book]) != nil) {
2819 database_ = database;
2821 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2822 [self addSubview:table_];
2824 [table_ setDataSource:self];
2825 [table_ setDelegate:self];
2829 - (void) setPackage:(Package *)package {
2830 if (package_ != nil) {
2831 [package_ autorelease];
2840 if (description_ != nil) {
2841 [description_ release];
2845 if (package != nil) {
2846 package_ = [package retain];
2847 name_ = [[package id] retain];
2849 NSString *description([package description]);
2850 if (description == nil)
2851 description = [package tagline];
2852 description_ = [GetTextView(description, 12, true) retain];
2854 [description_ setTextColor:Black_];
2856 [table_ reloadData];
2860 - (void) resetViewAnimated:(BOOL)animated {
2861 [table_ resetViewAnimated:animated];
2864 - (void) reloadData {
2865 [self setPackage:[database_ packageWithName:name_]];
2866 [self reloadButtons];
2871 /* Package Table {{{ */
2872 @interface PackageTable : RVPage {
2873 _transient Database *database_;
2877 NSMutableArray *packages_;
2878 NSMutableArray *sections_;
2879 UISectionList *list_;
2882 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2884 - (void) setDelegate:(id)delegate;
2885 - (void) setObject:(id)object;
2887 - (void) reloadData;
2888 - (void) resetCursor;
2892 @implementation PackageTable
2895 [list_ setDataSource:nil];
2900 [packages_ release];
2901 [sections_ release];
2906 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2907 return [sections_ count];
2910 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2911 return [[sections_ objectAtIndex:section] name];
2914 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2915 return [[sections_ objectAtIndex:section] row];
2918 - (int) numberOfRowsInTable:(UITable *)table {
2919 return [packages_ count];
2922 - (float) table:(UITable *)table heightForRow:(int)row {
2926 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2928 reusing = [[[PackageCell alloc] init] autorelease];
2929 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2933 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2937 - (void) tableRowSelected:(NSNotification *)notification {
2938 int row = [[notification object] selectedRow];
2942 Package *package = [packages_ objectAtIndex:row];
2943 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2944 [view setDelegate:delegate_];
2945 [view setPackage:package];
2946 [book_ pushPage:view];
2949 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2950 if ((self = [super initWithBook:book]) != nil) {
2951 database_ = database;
2952 title_ = [title retain];
2954 object_ = object == nil ? nil : [object retain];
2956 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2957 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2959 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2960 [list_ setDataSource:self];
2962 UITableColumn *column = [[[UITableColumn alloc]
2963 initWithTitle:@"Name"
2965 width:[self frame].size.width
2968 UITable *table = [list_ table];
2969 [table setSeparatorStyle:1];
2970 [table addTableColumn:column];
2971 [table setDelegate:self];
2972 [table setReusesTableCells:YES];
2974 [self addSubview:list_];
2979 - (void) setDelegate:(id)delegate {
2980 delegate_ = delegate;
2983 - (void) setObject:(id)object {
2989 object_ = [object retain];
2992 - (void) reloadData {
2993 NSArray *packages = [database_ packages];
2995 [packages_ removeAllObjects];
2996 [sections_ removeAllObjects];
2998 for (size_t i(0); i != [packages count]; ++i) {
2999 Package *package([packages objectAtIndex:i]);
3000 if ([[package performSelector:filter_ withObject:object_] boolValue])
3001 [packages_ addObject:package];
3004 [packages_ sortUsingSelector:@selector(compareByName:)];
3006 Section *section = nil;
3008 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3009 Package *package = [packages_ objectAtIndex:offset];
3010 NSString *name = [package index];
3012 if (section == nil || ![[section name] isEqualToString:name]) {
3013 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3014 [sections_ addObject:section];
3017 [section addToCount];
3023 - (NSString *) title {
3027 - (void) resetViewAnimated:(BOOL)animated {
3028 [list_ resetViewAnimated:animated];
3031 - (void) resetCursor {
3032 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3038 /* Browser Implementation {{{ */
3039 @implementation BrowserView
3042 WebView *webview = [webview_ webView];
3043 [webview setFrameLoadDelegate:nil];
3044 [webview setResourceLoadDelegate:nil];
3045 [webview setUIDelegate:nil];
3047 [scroller_ setDelegate:nil];
3048 [webview_ setDelegate:nil];
3050 [scroller_ release];
3053 [indicator_ release];
3059 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3060 NSMutableURLRequest *request = [NSMutableURLRequest
3063 timeoutInterval:30.0
3066 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3067 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3068 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3070 [self loadRequest:request];
3074 - (void) loadURL:(NSURL *)url {
3075 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3078 // XXX: this needs to add the headers
3079 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3083 - (void) loadRequest:(NSURLRequest *)request {
3084 [webview_ loadRequest:request];
3087 - (void) reloadURL {
3088 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3089 [urls_ removeLastObject];
3090 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3093 - (WebView *) webView {
3094 return [webview_ webView];
3097 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3098 [scroller_ setContentSize:frame.size];
3101 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3102 [self view:sender didSetFrame:frame];
3105 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3106 return [self _addHeadersToRequest:request];
3109 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3110 [self setBackButtonTitle:title_];
3111 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3112 [browser setDelegate:delegate_];
3113 [book_ pushPage:browser];
3114 [browser loadRequest:[self _addHeadersToRequest:request]];
3115 return [browser webView];
3118 - (void) webView:(WebView *)sender willClickElement:(id)element {
3119 if (![element respondsToSelector:@selector(href)])
3121 NSString *href = [element href];
3124 if ([href hasPrefix:@"apptapp://package/"]) {
3125 NSString *name = [href substringFromIndex:18];
3126 Package *package = [database_ packageWithName:name];
3127 if (package == nil) {
3128 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3129 initWithTitle:@"Cannot Locate Package"
3130 buttons:[NSArray arrayWithObjects:@"Close", nil]
3131 defaultButtonIndex:0
3136 [sheet setBodyText:[NSString stringWithFormat:
3137 @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
3140 [sheet popupAlertAnimated:YES];
3142 [self setBackButtonTitle:title_];
3143 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3144 [view setDelegate:delegate_];
3145 [view setPackage:package];
3146 [book_ pushPage:view];
3151 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3152 title_ = [title retain];
3153 [self setTitle:title];
3156 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3157 if ([frame parentFrame] != nil)
3161 [indicator_ startAnimation];
3162 [self reloadButtons];
3164 if (title_ != nil) {
3169 [self setTitle:@"Loading..."];
3171 WebView *webview = [webview_ webView];
3172 NSString *href = [webview mainFrameURL];
3173 [urls_ addObject:[NSURL URLWithString:href]];
3175 CGRect webrect = [scroller_ frame];
3176 webrect.size.height = 0;
3177 [webview_ setFrame:webrect];
3180 - (void) _finishLoading {
3182 [indicator_ stopAnimation];
3183 [self reloadButtons];
3186 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3187 if ([frame parentFrame] != nil)
3189 [self _finishLoading];
3192 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3193 if ([frame parentFrame] != nil)
3195 [self setTitle:[error localizedDescription]];
3196 [self _finishLoading];
3199 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3200 if ((self = [super initWithBook:book]) != nil) {
3201 database_ = database;
3204 struct CGRect bounds = [self bounds];
3206 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3207 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3208 [self addSubview:pinstripe];
3210 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3211 [self addSubview:scroller_];
3213 [scroller_ setScrollingEnabled:YES];
3214 [scroller_ setAdjustForContentSizeChange:YES];
3215 [scroller_ setClipsSubviews:YES];
3216 [scroller_ setAllowsRubberBanding:YES];
3217 [scroller_ setScrollDecelerationFactor:0.99];
3218 [scroller_ setDelegate:self];
3220 CGRect webrect = [scroller_ bounds];
3221 webrect.size.height = 0;
3223 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3224 [scroller_ addSubview:webview_];
3226 [webview_ setTilingEnabled:YES];
3227 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3228 [webview_ setAutoresizes:YES];
3229 [webview_ setDelegate:self];
3230 //[webview_ setEnabledGestures:2];
3232 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3233 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3234 [indicator_ setStyle:0];
3236 Package *package([database_ packageWithName:@"cydia"]);
3237 NSString *application = package == nil ? @"Cydia" : [NSString
3238 stringWithFormat:@"Cydia/%@",
3242 WebView *webview = [webview_ webView];
3243 [webview setApplicationNameForUserAgent:application];
3244 [webview setFrameLoadDelegate:self];
3245 [webview setResourceLoadDelegate:self];
3246 [webview setUIDelegate:self];
3248 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3252 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3256 - (void) _leftButtonClicked {
3257 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3258 initWithTitle:@"About Cydia Packager"
3259 buttons:[NSArray arrayWithObjects:@"Close", nil]
3260 defaultButtonIndex:0
3266 @"Copyright (C) 2008\n"
3267 "Jay Freeman (saurik)\n"
3268 "saurik@saurik.com\n"
3269 "http://www.saurik.com/\n"
3272 "http://www.theokorigroup.com/\n"
3274 "College of Creative Studies,\n"
3275 "University of California,\n"
3277 "http://www.ccs.ucsb.edu/"
3280 [sheet popupAlertAnimated:YES];
3283 - (void) _rightButtonClicked {
3287 - (NSString *) leftButtonTitle {
3291 - (NSString *) rightButtonTitle {
3292 return loading_ ? @"" : @"Reload";
3295 - (NSString *) title {
3299 - (NSString *) backButtonTitle {
3303 - (void) setPageActive:(BOOL)active {
3305 [book_ addSubview:indicator_];
3307 [indicator_ removeFromSuperview];
3310 - (void) resetViewAnimated:(BOOL)animated {
3316 @interface CYBook : RVBook <
3319 _transient Database *database_;
3321 UIProgressIndicator *indicator_;
3322 UITextLabel *prompt_;
3323 UIProgressBar *progress_;
3327 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3333 /* Install View {{{ */
3334 @interface InstallView : RVPage {
3335 _transient Database *database_;
3336 NSMutableArray *packages_;
3337 NSMutableArray *sections_;
3341 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3342 - (void) reloadData;
3346 @implementation InstallView
3349 [list_ setDataSource:nil];
3350 [list_ setDelegate:nil];
3352 [packages_ release];
3353 [sections_ release];
3358 - (int) numberOfRowsInTable:(UITable *)table {
3359 return [sections_ count] + 1;
3362 - (float) table:(UITable *)table heightForRow:(int)row {
3366 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3368 reusing = [[[SectionCell alloc] init] autorelease];
3369 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3373 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3377 - (void) tableRowSelected:(NSNotification *)notification {
3378 int row = [[notification object] selectedRow];
3387 title = @"All Packages";
3389 section = [sections_ objectAtIndex:(row - 1)];
3390 title = [section name];
3393 PackageTable *table = [[[PackageTable alloc]
3397 filter:@selector(isUninstalledInSection:)
3398 with:(section == nil ? nil : [section name])
3401 [table setDelegate:delegate_];
3403 [book_ pushPage:table];
3406 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3407 if ((self = [super initWithBook:book]) != nil) {
3408 database_ = database;
3410 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3411 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3413 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3414 [self addSubview:list_];
3416 UITableColumn *column = [[[UITableColumn alloc]
3417 initWithTitle:@"Name"
3419 width:[self frame].size.width
3422 [list_ setDataSource:self];
3423 [list_ setSeparatorStyle:1];
3424 [list_ addTableColumn:column];
3425 [list_ setDelegate:self];
3426 [list_ setReusesTableCells:YES];
3432 - (void) reloadData {
3433 NSArray *packages = [database_ packages];
3435 [packages_ removeAllObjects];
3436 [sections_ removeAllObjects];
3438 for (size_t i(0); i != [packages count]; ++i) {
3439 Package *package([packages objectAtIndex:i]);
3440 if ([package installed] == nil)
3441 [packages_ addObject:package];
3444 [packages_ sortUsingSelector:@selector(compareBySection:)];
3446 Section *section = nil;
3447 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3448 Package *package = [packages_ objectAtIndex:offset];
3449 NSString *name = [package section];
3451 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3452 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3453 [sections_ addObject:section];
3456 [section addToCount];
3462 - (void) resetViewAnimated:(BOOL)animated {
3463 [list_ resetViewAnimated:animated];
3466 - (NSString *) title {
3470 - (NSString *) backButtonTitle {
3476 /* Changes View {{{ */
3477 @interface ChangesView : RVPage {
3478 _transient Database *database_;
3479 NSMutableArray *packages_;
3480 NSMutableArray *sections_;
3481 UISectionList *list_;
3485 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3486 - (void) reloadData;
3490 @implementation ChangesView
3493 [[list_ table] setDelegate:nil];
3494 [list_ setDataSource:nil];
3496 [packages_ release];
3497 [sections_ release];
3502 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3503 return [sections_ count];
3506 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3507 return [[sections_ objectAtIndex:section] name];
3510 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3511 return [[sections_ objectAtIndex:section] row];
3514 - (int) numberOfRowsInTable:(UITable *)table {
3515 return [packages_ count];
3518 - (float) table:(UITable *)table heightForRow:(int)row {
3522 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3524 reusing = [[[PackageCell alloc] init] autorelease];
3525 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3529 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3533 - (void) tableRowSelected:(NSNotification *)notification {
3534 int row = [[notification object] selectedRow];
3537 Package *package = [packages_ objectAtIndex:row];
3538 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3539 [view setDelegate:delegate_];
3540 [view setPackage:package];
3541 [book_ pushPage:view];
3544 - (void) _leftButtonClicked {
3545 [(CYBook *)book_ update];
3546 [self reloadButtons];
3549 - (void) _rightButtonClicked {
3550 [delegate_ distUpgrade];
3553 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3554 if ((self = [super initWithBook:book]) != nil) {
3555 database_ = database;
3557 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3558 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3560 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3561 [self addSubview:list_];
3563 [list_ setShouldHideHeaderInShortLists:NO];
3564 [list_ setDataSource:self];
3565 //[list_ setSectionListStyle:1];
3567 UITableColumn *column = [[[UITableColumn alloc]
3568 initWithTitle:@"Name"
3570 width:[self frame].size.width
3573 UITable *table = [list_ table];
3574 [table setSeparatorStyle:1];
3575 [table addTableColumn:column];
3576 [table setDelegate:self];
3577 [table setReusesTableCells:YES];
3583 - (void) reloadData {
3584 NSArray *packages = [database_ packages];
3586 [packages_ removeAllObjects];
3587 [sections_ removeAllObjects];
3589 for (size_t i(0); i != [packages count]; ++i) {
3590 Package *package([packages objectAtIndex:i]);
3591 if ([package installed] == nil || [package upgradable])
3592 [packages_ addObject:package];
3595 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3597 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3598 Section *section = nil;
3601 bool unseens = false;
3603 CFLocaleRef locale = CFLocaleCopyCurrent();
3604 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3606 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3607 Package *package = [packages_ objectAtIndex:offset];
3609 if ([package upgradable]) {
3611 [upgradable addToCount];
3614 NSDate *seen = [package seen];
3619 name = [@"n/a ?" retain];
3621 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3624 if (section == nil || ![[section name] isEqualToString:name]) {
3625 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3626 [sections_ addObject:section];
3630 [section addToCount];
3634 CFRelease(formatter);
3638 Section *last = [sections_ lastObject];
3639 size_t count = [last count];
3640 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3641 [sections_ removeLastObject];
3645 [sections_ insertObject:upgradable atIndex:0];
3648 [self reloadButtons];
3651 - (void) resetViewAnimated:(BOOL)animated {
3652 [list_ resetViewAnimated:animated];
3655 - (NSString *) leftButtonTitle {
3656 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3659 - (NSString *) rightButtonTitle {
3660 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3663 - (NSString *) title {
3669 /* Search View {{{ */
3670 @protocol SearchViewDelegate
3671 - (void) showKeyboard:(BOOL)show;
3674 @interface SearchView : RVPage {
3676 UISearchField *field_;
3677 UITransitionView *transition_;
3678 PackageTable *table_;
3679 UIPreferencesTable *advanced_;
3684 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3685 - (void) reloadData;
3689 @implementation SearchView
3693 [[field_ textTraits] setEditingDelegate:nil];
3695 [field_ setDelegate:nil];
3697 [accessory_ release];
3699 [transition_ release];
3701 [advanced_ release];
3706 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3707 [delegate_ showKeyboard:YES];
3708 [table_ setEnabled:NO];
3709 [self addSubview:dimmed_];
3712 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3713 [dimmed_ removeFromSuperview];
3714 [table_ setEnabled:YES];
3715 [delegate_ showKeyboard:NO];
3718 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3719 NSString *text([field_ text]);
3720 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3723 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3724 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3728 [field_ resignFirstResponder];
3732 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3733 if ((self = [super initWithBook:book]) != nil) {
3734 CGRect pageBounds = [book_ pageBounds];
3736 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
3737 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3738 [self addSubview:pinstripe];*/
3740 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3741 CGColor dimmed(space_, 0, 0, 0, 0.5);
3742 [dimmed_ setBackgroundColor:dimmed];
3744 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3745 [self addSubview:transition_];
3747 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3749 table_ = [[PackageTable alloc]
3753 filter:@selector(isSearchedForBy:)
3757 [transition_ transition:0 toView:table_];
3759 CGRect cnfrect = {{3, 36}, {17, 18}};
3762 area.origin.x = cnfrect.size.width + 12;
3764 area.size.width = [self bounds].size.width - area.origin.x - 18;
3765 area.size.height = [UISearchField defaultHeight];
3767 field_ = [[UISearchField alloc] initWithFrame:area];
3769 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3770 [field_ setFont:font];
3773 [field_ setPlaceholder:@"Package Names & Descriptions"];
3774 [field_ setPaddingTop:5];
3775 [field_ setDelegate:self];
3778 UITextTraits *traits = [field_ textTraits];
3779 [traits setEditingDelegate:self];
3780 [traits setReturnKeyType:6];
3781 [traits setAutoCapsType:0];
3782 [traits setAutoCorrectionType:1];
3785 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3786 [configure setShowPressFeedback:YES];
3787 [configure setImage:[UIImage applicationImageNamed:@"configure.png"]];
3788 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3790 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3791 [accessory_ addSubview:field_];
3792 [accessory_ addSubview:configure];
3797 LKAnimation *animation = [LKTransition animation];
3798 [animation setType:@"oglFlip"];
3799 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3800 [animation setFillMode:@"extended"];
3801 [animation setTransitionFlags:3];
3802 [animation setDuration:10];
3803 [animation setSpeed:0.35];
3804 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
3805 [[transition_ _layer] addAnimation:animation forKey:0];
3806 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3807 flipped_ = !flipped_;
3810 - (void) configurePushed {
3811 [field_ resignFirstResponder];
3815 - (void) resetViewAnimated:(BOOL)animated {
3818 [table_ resetViewAnimated:animated];
3821 - (void) reloadData {
3824 [table_ setObject:[field_ text]];
3825 [table_ reloadData];
3826 [table_ resetCursor];
3829 - (UIView *) accessoryView {
3833 - (NSString *) title {
3837 - (NSString *) backButtonTitle {
3844 @implementation CYBook
3848 [indicator_ release];
3850 [progress_ release];
3859 [navbar_ setPrompt:@""];
3860 [navbar_ addSubview:overlay_];
3861 [indicator_ startAnimation];
3862 [prompt_ setText:@"Updating Database..."];
3863 [progress_ setProgress:0];
3868 detachNewThreadSelector:@selector(_update)
3877 [overlay_ removeFromSuperview];
3878 [indicator_ stopAnimation];
3879 [delegate_ reloadData];
3881 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3884 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3885 if ((self = [super initWithFrame:frame]) != nil) {
3886 database_ = database;
3888 CGRect ovrrect = [navbar_ bounds];
3889 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3891 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3893 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3894 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3895 CGRect indrect = {{indoffset, indoffset}, indsize};
3897 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3898 [indicator_ setStyle:2];
3899 [overlay_ addSubview:indicator_];
3901 CGSize prmsize = {200, indsize.width};
3904 indoffset * 2 + indsize.width,
3905 (ovrrect.size.height - prmsize.height) / 2
3908 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3910 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3912 [prompt_ setColor:White_];
3913 [prompt_ setBackgroundColor:Clear_];
3914 [prompt_ setFont:font];
3918 [overlay_ addSubview:prompt_];
3920 CGSize prgsize = {75, 100};
3923 ovrrect.size.width - prgsize.width - 10,
3924 (ovrrect.size.height - prgsize.height) / 2
3927 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3928 [progress_ setStyle:0];
3929 [overlay_ addSubview:progress_];
3934 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3937 status.setDelegate(self);
3939 [database_ updateWithStatus:status];
3942 performSelectorOnMainThread:@selector(_update_)
3950 - (void) setProgressError:(NSString *)error {
3952 performSelectorOnMainThread:@selector(_setProgressError:)
3958 - (void) setProgressTitle:(NSString *)title {
3960 performSelectorOnMainThread:@selector(_setProgressTitle:)
3966 - (void) setProgressPercent:(float)percent {
3969 - (void) addProgressOutput:(NSString *)output {
3971 performSelectorOnMainThread:@selector(_addProgressOutput:)
3977 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3981 - (void) _setProgressError:(NSString *)error {
3982 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
3985 - (void) _setProgressTitle:(NSString *)title {
3986 [prompt_ setText:[title stringByAppendingString:@"..."]];
3989 - (void) _addProgressOutput:(NSString *)output {
3994 @interface Cydia : UIApplication <
3995 ConfirmationViewDelegate,
3996 ProgressViewDelegate,
4005 UIButtonBar *buttonbar_;
4007 ConfirmationView *confirm_;
4009 Database *database_;
4010 ProgressView *progress_;
4014 UIKeyboard *keyboard_;
4019 @implementation Cydia
4021 - (void) _reloadData {
4022 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4023 [hud setText:@"Reloading Data"];
4024 [overlay_ addSubview:hud];
4027 [database_ reloadData];
4031 if (Packages_ == nil) {
4032 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
4033 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4038 NSArray *packages = [database_ packages];
4039 for (int i(0), e([packages count]); i != e; ++i) {
4040 Package *package = [packages objectAtIndex:i];
4041 if ([package upgradable])
4046 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4047 [buttonbar_ setBadgeValue:badge forButton:3];
4048 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4049 [buttonbar_ setBadgeAnimated:YES forButton:3];
4050 [self setApplicationBadge:badge];
4052 [buttonbar_ setBadgeValue:nil forButton:3];
4053 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4054 [buttonbar_ setBadgeAnimated:NO forButton:3];
4055 [self removeApplicationBadge];
4058 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4062 [hud removeFromSuperview];*/
4065 - (void) reloadData {
4066 @synchronized (self) {
4067 if (confirm_ == nil)
4073 pkgProblemResolver *resolver = [database_ resolver];
4075 resolver->InstallProtect();
4076 if (!resolver->Resolve(true))
4081 [database_ prepare];
4083 if ([database_ cache]->BrokenCount() == 0)
4084 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4086 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4087 NSArray *packages = [database_ packages];
4089 for (size_t i(0); i != [packages count]; ++i) {
4090 Package *package = [packages objectAtIndex:i];
4091 if ([package broken])
4092 [broken addObject:[package name]];
4095 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4096 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4097 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4098 defaultButtonIndex:0
4103 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4104 [sheet popupAlertAnimated:YES];
4110 - (void) installPackage:(Package *)package {
4111 @synchronized (self) {
4118 - (void) removePackage:(Package *)package {
4119 @synchronized (self) {
4126 - (void) distUpgrade {
4127 @synchronized (self) {
4128 [database_ upgrade];
4134 @synchronized (self) {
4142 [overlay_ removeFromSuperview];
4146 detachNewThreadSelector:@selector(perform)
4153 - (void) bootstrap_ {
4155 [database_ upgrade];
4156 [database_ prepare];
4157 [database_ perform];
4160 - (void) bootstrap {
4162 detachNewThreadSelector:@selector(bootstrap_)
4165 title:@"Bootstrap Install..."
4169 - (void) progressViewIsComplete:(ProgressView *)progress {
4170 @synchronized (self) {
4173 if (confirm_ != nil) {
4174 [underlay_ addSubview:overlay_];
4175 [confirm_ removeFromSuperview];
4182 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4186 - (void) setPage:(RVPage *)page {
4187 [page setDelegate:self];
4188 [book_ setPage:page];
4191 - (RVPage *) _setNewsPage {
4192 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4193 [self setPage:browser];
4194 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4198 - (void) buttonBarItemTapped:(id)sender {
4199 unsigned tag = [sender tag];
4203 [self _setNewsPage];
4207 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4211 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4215 [self setPage:[[[PackageTable alloc]
4219 filter:@selector(isInstalledInSection:)
4225 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4235 - (void) applicationWillSuspend {
4236 [super applicationWillSuspend];
4239 if (FW_LEAST(1,1,3))
4240 notify_post("com.apple.language.changed");
4242 system("launchctl stop com.apple.SpringBoard");
4245 - (void) applicationDidFinishLaunching:(id)unused {
4246 _assert(pkgInitConfig(*_config));
4247 _assert(pkgInitSystem(*_config, _system));
4252 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4253 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4255 [window_ orderFront: self];
4256 [window_ makeKey: self];
4257 [window_ _setHidden: NO];
4259 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
4260 [window_ setContentView:progress_];
4262 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4263 [progress_ setContentView:underlay_];
4265 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4268 [underlay_ addSubview:overlay_];
4270 database_ = [[Database alloc] init];
4271 [database_ setDelegate:progress_];
4273 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4274 0, 0, screenrect.size.width, screenrect.size.height - 48
4275 ) database:database_];
4277 [book_ setDelegate:self];
4279 [overlay_ addSubview:book_];
4281 NSArray *buttonitems = [NSArray arrayWithObjects:
4282 [NSDictionary dictionaryWithObjectsAndKeys:
4283 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4284 @"news-up.png", kUIButtonBarButtonInfo,
4285 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4286 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4287 self, kUIButtonBarButtonTarget,
4288 @"News", kUIButtonBarButtonTitle,
4289 @"0", kUIButtonBarButtonType,
4292 [NSDictionary dictionaryWithObjectsAndKeys:
4293 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4294 @"install-up.png", kUIButtonBarButtonInfo,
4295 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4296 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4297 self, kUIButtonBarButtonTarget,
4298 @"Install", kUIButtonBarButtonTitle,
4299 @"0", kUIButtonBarButtonType,
4302 [NSDictionary dictionaryWithObjectsAndKeys:
4303 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4304 @"changes-up.png", kUIButtonBarButtonInfo,
4305 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4306 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4307 self, kUIButtonBarButtonTarget,
4308 @"Changes", kUIButtonBarButtonTitle,
4309 @"0", kUIButtonBarButtonType,
4312 [NSDictionary dictionaryWithObjectsAndKeys:
4313 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4314 @"manage-up.png", kUIButtonBarButtonInfo,
4315 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4316 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4317 self, kUIButtonBarButtonTarget,
4318 @"Manage", kUIButtonBarButtonTitle,
4319 @"0", kUIButtonBarButtonType,
4322 [NSDictionary dictionaryWithObjectsAndKeys:
4323 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4324 @"search-up.png", kUIButtonBarButtonInfo,
4325 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4326 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4327 self, kUIButtonBarButtonTarget,
4328 @"Search", kUIButtonBarButtonTitle,
4329 @"0", kUIButtonBarButtonType,
4333 buttonbar_ = [[UIButtonBar alloc]
4335 withFrame:CGRectMake(
4336 0, screenrect.size.height - 48,
4337 screenrect.size.width, 48
4339 withItemList:buttonitems
4342 [buttonbar_ setDelegate:self];
4343 [buttonbar_ setBarStyle:1];
4344 [buttonbar_ setButtonBarTrackingMode:2];
4346 int buttons[5] = {1, 2, 3, 4, 5};
4347 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4348 [buttonbar_ showButtonGroup:0 withDuration:0];
4350 for (int i = 0; i != 5; ++i)
4351 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4352 i * 64 + 2, 1, 60, 48
4355 [buttonbar_ showSelectionForButton:1];
4356 [overlay_ addSubview:buttonbar_];
4358 [UIKeyboard initImplementationNow];
4359 CGSize keysize = [UIKeyboard defaultSize];
4360 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4361 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4362 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4367 [progress_ resetView];
4372 [self _setNewsPage];
4375 - (void) showKeyboard:(BOOL)show {
4377 [overlay_ addSubview:keyboard_];
4379 [keyboard_ removeFromSuperview];
4382 - (void) slideUp:(UIAlertSheet *)alert {
4383 [alert presentSheetFromButtonBar:buttonbar_];
4388 void AddPreferences(NSString *plist) {
4389 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4391 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4392 _assert(settings != NULL);
4393 NSMutableArray *items = [settings objectForKey:@"items"];
4397 for (size_t i(0); i != [items count]; ++i) {
4398 NSMutableDictionary *item([items objectAtIndex:i]);
4399 NSString *label = [item objectForKey:@"label"];
4400 if (label != nil && [label isEqualToString:@"Cydia"]) {
4407 for (size_t i(0); i != [items count]; ++i) {
4408 NSDictionary *item([items objectAtIndex:i]);
4409 NSString *label = [item objectForKey:@"label"];
4410 if (label != nil && [label isEqualToString:@"General"]) {
4411 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4412 @"CydiaSettings", @"bundle",
4413 @"PSLinkCell", @"cell",
4414 [NSNumber numberWithBool:YES], @"hasIcon",
4415 [NSNumber numberWithBool:YES], @"isController",
4417 nil] atIndex:(i + 1)];
4423 _assert([settings writeToFile:plist atomically:YES] == YES);
4430 id Alloc_(id self, SEL selector) {
4431 id object = alloc_(self, selector);
4432 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4437 id Dealloc_(id self, SEL selector) {
4438 id object = dealloc_(self, selector);
4439 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4443 int main(int argc, char *argv[]) {
4444 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4446 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4448 Home_ = NSHomeDirectory();
4451 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4452 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4453 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4454 Sounds_Keyboard_ = [keyboard boolValue];
4460 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4461 alloc_ = alloc->method_imp;
4462 alloc->method_imp = (IMP) &Alloc_;*/
4464 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4465 dealloc_ = dealloc->method_imp;
4466 dealloc->method_imp = (IMP) &Dealloc_;*/
4468 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4469 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4470 Firmware_ = strdup([prover UTF8String]);
4471 NSArray *versions = [prover componentsSeparatedByString:@"."];
4472 int count = [versions count];
4473 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4474 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4475 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4480 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4481 char *machine = new char[size];
4482 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4485 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4486 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4487 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4488 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4492 IOObjectRelease(service);
4495 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4496 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4498 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4499 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4501 Packages_ = [Metadata_ objectForKey:@"Packages"];
4503 setenv("CYDIA", "", _not(int));
4504 if (access("/User", F_OK) != 0)
4505 system("/usr/libexec/cydia/firmware.sh");
4506 system("dpkg --configure -a");
4508 space_ = CGColorSpaceCreateDeviceRGB();
4510 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4511 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4512 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4513 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4515 int value = UIApplicationMain(argc, argv, [Cydia class]);
4517 CGColorSpaceRelease(space_);