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];
515 @protocol ProgressDelegate
516 - (void) setProgressError:(NSString *)error;
517 - (void) setProgressTitle:(NSString *)title;
518 - (void) setProgressPercent:(float)percent;
519 - (void) addProgressOutput:(NSString *)output;
522 @protocol CydiaDelegate
523 - (void) installPackage:(Package *)package;
524 - (void) removePackage:(Package *)package;
525 - (void) slideUp:(UIAlertSheet *)alert;
526 - (void) distUpgrade;
529 /* Status Delegation {{{ */
531 public pkgAcquireStatus
534 _transient id<ProgressDelegate> delegate_;
542 void setDelegate(id delegate) {
543 delegate_ = delegate;
546 virtual bool MediaChange(std::string media, std::string drive) {
550 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
553 virtual void Fetch(pkgAcquire::ItemDesc &item) {
554 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
557 virtual void Done(pkgAcquire::ItemDesc &item) {
560 virtual void Fail(pkgAcquire::ItemDesc &item) {
562 item.Owner->Status == pkgAcquire::Item::StatIdle ||
563 item.Owner->Status == pkgAcquire::Item::StatDone
567 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
570 virtual bool Pulse(pkgAcquire *Owner) {
571 bool value = pkgAcquireStatus::Pulse(Owner);
574 double(CurrentBytes + CurrentItems) /
575 double(TotalBytes + TotalItems)
578 [delegate_ setProgressPercent:percent];
582 virtual void Start() {
585 virtual void Stop() {
589 /* Progress Delegation {{{ */
594 _transient id<ProgressDelegate> delegate_;
597 virtual void Update() {
598 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
599 [delegate_ setProgressPercent:(Percent / 100)];
608 void setDelegate(id delegate) {
609 delegate_ = delegate;
612 virtual void Done() {
613 [delegate_ setProgressPercent:1];
618 /* Database Interface {{{ */
619 @interface Database : NSObject {
621 pkgRecords *records_;
622 pkgProblemResolver *resolver_;
623 pkgAcquire *fetcher_;
625 SPtr<pkgPackageManager> manager_;
626 pkgSourceList *list_;
628 NSMutableDictionary *sources_;
629 NSMutableArray *packages_;
631 _transient id delegate_;
637 - (void) _readStatus:(NSNumber *)fd;
638 - (void) _readOutput:(NSNumber *)fd;
640 - (Package *) packageWithName:(NSString *)name;
643 - (pkgCacheFile &) cache;
644 - (pkgRecords *) records;
645 - (pkgProblemResolver *) resolver;
646 - (pkgAcquire &) fetcher;
647 - (NSArray *) packages;
655 - (void) updateWithStatus:(Status &)status;
657 - (void) setDelegate:(id)delegate;
658 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
662 /* Source Class {{{ */
663 @interface Source : NSObject {
664 NSString *description_;
669 NSString *distribution_;
673 NSString *defaultIcon_;
678 - (Source *) initWithMetaIndex:(metaIndex *)index;
683 - (NSString *) distribution;
686 - (NSString *) description;
687 - (NSString *) label;
688 - (NSString *) origin;
689 - (NSString *) version;
691 - (NSString *) defaultIcon;
694 @implementation Source
698 [distribution_ release];
701 if (description_ != nil)
702 [description_ release];
709 if (defaultIcon_ != nil)
710 [defaultIcon_ release];
715 - (Source *) initWithMetaIndex:(metaIndex *)index {
716 if ((self = [super init]) != nil) {
717 trusted_ = index->IsTrusted();
719 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
720 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
721 type_ = [[NSString stringWithCString:index->GetType()] retain];
729 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
730 if (dindex != NULL) {
731 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
733 while (std::getline(release, line)) {
734 std::string::size_type colon(line.find(':'));
735 if (colon == std::string::npos)
738 std::string name(line.substr(0, colon));
739 std::string value(line.substr(colon + 1));
740 while (!value.empty() && value[0] == ' ')
741 value = value.substr(1);
743 if (name == "Default-Icon")
744 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
745 else if (name == "Description")
746 description_ = [[NSString stringWithCString:value.c_str()] retain];
747 else if (name == "Label")
748 label_ = [[NSString stringWithCString:value.c_str()] retain];
749 else if (name == "Origin")
750 origin_ = [[NSString stringWithCString:value.c_str()] retain];
751 else if (name == "Version")
752 version_ = [[NSString stringWithCString:value.c_str()] retain];
766 - (NSString *) distribution {
767 return distribution_;
770 - (NSString *) type {
774 - (NSString *) description {
778 - (NSString *) label {
782 - (NSString *) origin {
786 - (NSString *) version {
790 - (NSString *) defaultIcon {
796 /* Package Class {{{ */
797 NSString *Scour(const char *field, const char *begin, const char *end) {
798 size_t i(0), l(strlen(field));
801 const char *name = begin + i;
802 const char *colon = name + l;
803 const char *value = colon + 1;
808 memcmp(name, field, l) == 0
810 while (value != end && value[0] == ' ')
812 const char *line = std::find(value, end, '\n');
813 while (line != value && line[-1] == ' ')
815 return [NSString stringWithCString:value length:(line - value)];
817 begin = std::find(begin, end, '\n');
825 @interface Package : NSObject {
826 pkgCache::PkgIterator iterator_;
827 _transient Database *database_;
828 pkgCache::VerIterator version_;
829 pkgCache::VerFileIterator file_;
833 NSString *installed_;
842 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
843 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
845 - (NSString *) section;
846 - (Address *) maintainer;
848 - (NSString *) description;
849 - (NSString *) index;
853 - (NSString *) latest;
854 - (NSString *) installed;
861 - (NSString *) tagline;
863 - (NSString *) website;
867 - (BOOL) matches:(NSString *)text;
869 - (NSComparisonResult) compareByName:(Package *)package;
870 - (NSComparisonResult) compareBySection:(Package *)package;
871 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
872 - (NSComparisonResult) compareForChanges:(Package *)package;
877 - (NSNumber *) isSearchedForBy:(NSString *)search;
878 - (NSNumber *) isInstalledInSection:(NSString *)section;
879 - (NSNumber *) isUninstalledInSection:(NSString *)section;
883 @implementation Package
887 if (installed_ != nil)
888 [installed_ release];
904 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
905 if ((self = [super init]) != nil) {
906 iterator_ = iterator;
907 database_ = database;
912 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
914 const char *begin, *end;
915 parser->GetRec(begin, end);
917 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
918 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
920 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
921 name_ = Scour("Name", begin, end);
923 name_ = [name_ retain];
924 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
925 icon_ = Scour("Icon", begin, end);
927 icon_ = [icon_ retain];
928 website_ = Scour("Website", begin, end);
930 website_ = [website_ retain];
932 source_ = [[database_ getSource:file_.File()] retain];
934 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
935 if (metadata == nil || [metadata count] == 0) {
936 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
940 [Packages_ setObject:metadata forKey:id_];
945 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
946 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
947 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
948 return [[[Package alloc]
949 initWithIterator:iterator
957 - (NSString *) section {
958 const char *section = iterator_.Section();
959 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
962 - (Address *) maintainer {
963 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
964 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
968 return version_->InstalledSize;
971 - (NSString *) description {
972 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
973 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
975 NSArray *lines = [description componentsSeparatedByString:@"\n"];
976 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
977 if ([lines count] < 2)
980 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
981 for (size_t i(1); i != [lines count]; ++i) {
982 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
983 [trimmed addObject:trim];
986 return [trimmed componentsJoinedByString:@"\n"];
989 - (NSString *) index {
990 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
991 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
995 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
998 - (NSString *) latest {
1002 - (NSString *) installed {
1006 - (BOOL) upgradable {
1007 if (NSString *installed = [self installed])
1008 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1010 return [self essential];
1013 - (BOOL) essential {
1014 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1018 return (*[database_ cache])[iterator_].InstBroken();
1025 - (NSString *) name {
1026 return name_ == nil ? id_ : name_;
1029 - (NSString *) tagline {
1033 - (NSString *) icon {
1037 - (NSString *) website {
1041 - (Source *) source {
1045 - (BOOL) matches:(NSString *)text {
1051 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1052 if (range.location != NSNotFound)
1055 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1056 if (range.location != NSNotFound)
1059 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1060 if (range.location != NSNotFound)
1066 - (NSComparisonResult) compareByName:(Package *)package {
1067 NSString *lhs = [self name];
1068 NSString *rhs = [package name];
1070 if ([lhs length] != 0 && [rhs length] != 0) {
1071 unichar lhc = [lhs characterAtIndex:0];
1072 unichar rhc = [rhs characterAtIndex:0];
1074 if (isalpha(lhc) && !isalpha(rhc))
1075 return NSOrderedAscending;
1076 else if (!isalpha(lhc) && isalpha(rhc))
1077 return NSOrderedDescending;
1080 return [lhs caseInsensitiveCompare:rhs];
1083 - (NSComparisonResult) compareBySection:(Package *)package {
1084 NSString *lhs = [self section];
1085 NSString *rhs = [package section];
1087 if (lhs == NULL && rhs != NULL)
1088 return NSOrderedAscending;
1089 else if (lhs != NULL && rhs == NULL)
1090 return NSOrderedDescending;
1091 else if (lhs != NULL && rhs != NULL) {
1092 NSComparisonResult result = [lhs compare:rhs];
1093 if (result != NSOrderedSame)
1097 return NSOrderedSame;
1100 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1101 NSString *lhs = [self section];
1102 NSString *rhs = [package section];
1104 if (lhs == NULL && rhs != NULL)
1105 return NSOrderedAscending;
1106 else if (lhs != NULL && rhs == NULL)
1107 return NSOrderedDescending;
1108 else if (lhs != NULL && rhs != NULL) {
1109 NSComparisonResult result = [lhs compare:rhs];
1110 if (result != NSOrderedSame)
1114 return [self compareByName:package];
1117 - (NSComparisonResult) compareForChanges:(Package *)package {
1118 BOOL lhs = [self upgradable];
1119 BOOL rhs = [package upgradable];
1122 return lhs ? NSOrderedAscending : NSOrderedDescending;
1124 switch ([[self seen] compare:[package seen]]) {
1125 case NSOrderedAscending:
1126 return NSOrderedDescending;
1129 case NSOrderedDescending:
1130 return NSOrderedAscending;
1136 return [self compareByName:package];
1140 pkgProblemResolver *resolver = [database_ resolver];
1141 resolver->Clear(iterator_);
1142 resolver->Protect(iterator_);
1143 pkgCacheFile &cache([database_ cache]);
1144 cache->MarkInstall(iterator_, false);
1145 pkgDepCache::StateCache &state((*cache)[iterator_]);
1146 if (!state.Install())
1147 cache->SetReInstall(iterator_, true);
1151 pkgProblemResolver *resolver = [database_ resolver];
1152 resolver->Clear(iterator_);
1153 resolver->Protect(iterator_);
1154 resolver->Remove(iterator_);
1155 [database_ cache]->MarkDelete(iterator_, true);
1158 - (NSNumber *) isSearchedForBy:(NSString *)search {
1159 return [NSNumber numberWithBool:[self matches:search]];
1162 - (NSNumber *) isInstalledInSection:(NSString *)section {
1163 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1166 - (NSNumber *) isUninstalledInSection:(NSString *)section {
1167 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1172 /* Section Class {{{ */
1173 @interface Section : NSObject {
1179 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1180 - (NSString *) name;
1183 - (void) addToCount;
1187 @implementation Section
1194 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1195 if ((self = [super init]) != nil) {
1196 name_ = [name retain];
1201 - (NSString *) name {
1213 - (void) addToCount {
1220 /* Database Implementation {{{ */
1221 @implementation Database
1228 - (void) _readStatus:(NSNumber *)fd {
1229 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1231 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1232 std::istream is(&ib);
1237 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1239 pcre_extra *study = NULL;
1241 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1242 int matches[(capture + 1) * 3];
1244 while (std::getline(is, line)) {
1245 const char *data(line.c_str());
1247 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1249 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1252 [delegate_ setProgressPercent:(percent / 100)];
1254 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1255 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1257 if (type == "pmerror")
1258 [delegate_ setProgressError:string];
1259 else if (type == "pmstatus")
1260 [delegate_ setProgressTitle:string];
1261 else if (type == "pmconffile")
1263 else _assert(false);
1270 - (void) _readOutput:(NSNumber *)fd {
1271 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1273 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1274 std::istream is(&ib);
1277 while (std::getline(is, line))
1278 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1284 - (Package *) packageWithName:(NSString *)name {
1285 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1286 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1289 - (Database *) init {
1290 if ((self = [super init]) != nil) {
1296 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1297 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1301 _assert(pipe(fds) != -1);
1305 detachNewThreadSelector:@selector(_readStatus:)
1307 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1310 _assert(pipe(fds) != -1);
1311 _assert(dup2(fds[1], 1) != -1);
1312 _assert(close(fds[1]) != -1);
1315 detachNewThreadSelector:@selector(_readOutput:)
1317 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1322 - (pkgCacheFile &) cache {
1326 - (pkgRecords *) records {
1330 - (pkgProblemResolver *) resolver {
1334 - (pkgAcquire &) fetcher {
1338 - (NSArray *) packages {
1342 - (void) reloadData {
1352 if (!cache_.Open(progress_, true)) {
1353 fprintf(stderr, "repairing corrupted database...\n");
1355 [self updateWithStatus:status_];
1356 _assert(cache_.Open(progress_, true));
1359 now_ = [[NSDate date] retain];
1361 records_ = new pkgRecords(cache_);
1362 resolver_ = new pkgProblemResolver(cache_);
1363 fetcher_ = new pkgAcquire(&status_);
1366 list_ = new pkgSourceList();
1367 _assert(list_->ReadMainList());
1369 [sources_ removeAllObjects];
1370 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1371 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1372 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1374 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1375 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1379 [packages_ removeAllObjects];
1380 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1381 if (Package *package = [Package packageWithIterator:iterator database:self])
1382 if ([package source] != nil || [package installed] != nil)
1383 [packages_ addObject:package];
1387 pkgRecords records(cache_);
1389 lock_ = new FileFd();
1390 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1391 _assert(!_error->PendingError());
1394 // XXX: explain this with an error message
1395 _assert(list.ReadMainList());
1397 manager_ = (_system->CreatePM(cache_));
1398 _assert(manager_->GetArchives(fetcher_, &list, &records));
1399 _assert(!_error->PendingError());
1404 _assert(list.ReadMainList());
1406 /*std::map<std::string> before;
1408 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source)
1409 before.add((*source)->GetURI().c_str());
1412 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1416 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1418 if (result == pkgPackageManager::Failed)
1420 if (_error->PendingError())
1422 if (result != pkgPackageManager::Completed)
1425 _assert(list.ReadMainList());
1429 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1430 _assert(pkgApplyStatus(cache_));
1432 if (cache_->BrokenCount() != 0) {
1433 _assert(pkgFixBroken(cache_));
1434 _assert(cache_->BrokenCount() == 0);
1435 _assert(pkgMinimizeUpgrade(cache_));
1438 _assert(pkgDistUpgrade(cache_));
1442 [self updateWithStatus:status_];
1445 - (void) updateWithStatus:(Status &)status {
1447 _assert(list.ReadMainList());
1450 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1451 _assert(!_error->PendingError());
1453 pkgAcquire fetcher(&status);
1454 _assert(list.GetIndexes(&fetcher));
1456 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1457 bool failed = false;
1458 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1459 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1460 (*item)->Finished();
1464 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1465 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1466 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1469 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1473 - (void) setDelegate:(id)delegate {
1474 delegate_ = delegate;
1475 status_.setDelegate(delegate);
1476 progress_.setDelegate(delegate);
1479 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1480 pkgIndexFile *index(NULL);
1481 list_->FindIndex(file, index);
1482 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1488 /* RVPage Interface {{{ */
1491 @interface RVPage : UIView {
1492 _transient RVBook *book_;
1493 _transient id delegate_;
1496 - (NSString *) title;
1497 - (NSString *) backButtonTitle;
1498 - (NSString *) rightButtonTitle;
1499 - (NSString *) leftButtonTitle;
1500 - (UIView *) accessoryView;
1502 - (void) _rightButtonClicked;
1503 - (void) _leftButtonClicked;
1505 - (void) setPageActive:(BOOL)active;
1506 - (void) resetViewAnimated:(BOOL)animated;
1508 - (void) setTitle:(NSString *)title;
1509 - (void) setBackButtonTitle:(NSString *)title;
1511 - (void) reloadButtons;
1512 - (void) reloadData;
1514 - (id) initWithBook:(RVBook *)book;
1516 - (void) setDelegate:(id)delegate;
1520 /* Reset View {{{ */
1521 @protocol RVDelegate
1522 - (void) setPageActive:(BOOL)active with:(id)object;
1523 - (void) resetViewAnimated:(BOOL)animated with:(id)object;
1524 - (void) reloadDataWith:(id)object;
1527 @interface RVBook : UIView {
1528 NSMutableArray *pages_;
1529 UINavigationBar *navbar_;
1530 UITransitionView *transition_;
1532 _transient id delegate_;
1535 - (id) initWithFrame:(CGRect)frame;
1536 - (void) setDelegate:(id)delegate;
1538 - (void) setPage:(RVPage *)page;
1540 - (void) pushPage:(RVPage *)page;
1541 - (void) popPages:(unsigned)pages;
1543 - (void) setPrompt:(NSString *)prompt;
1545 - (void) resetViewAnimated:(BOOL)animated;
1546 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
1548 - (void) setTitle:(NSString *)title forPage:(RVPage *)page;
1549 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
1550 - (void) reloadButtonsForPage:(RVPage *)page;
1552 - (void) reloadData;
1554 - (CGRect) pageBounds;
1558 @implementation RVBook
1561 [navbar_ setDelegate:nil];
1565 [transition_ release];
1569 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1570 _assert([pages_ count] != 0);
1571 RVPage *page = [pages_ lastObject];
1573 case 0: [page _rightButtonClicked]; break;
1574 case 1: [page _leftButtonClicked]; break;
1578 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
1579 _assert([pages_ count] != 0);
1581 [[pages_ lastObject] setPageActive:NO];
1582 [pages_ removeLastObject];
1584 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1587 - (id) initWithFrame:(CGRect)frame {
1588 if ((self = [super initWithFrame:frame]) != nil) {
1589 pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
1591 struct CGRect bounds = [self bounds];
1592 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
1593 CGRect navrect = {{0, 0}, navsize};
1595 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1596 [self addSubview:navbar_];
1598 [navbar_ setBarStyle:1];
1599 [navbar_ setDelegate:self];
1601 [navbar_ setPrompt:@""];
1603 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
1604 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
1607 [self addSubview:transition_];
1611 - (void) setDelegate:(id)delegate {
1612 delegate_ = delegate;
1615 - (void) setPage:(RVPage *)page {
1616 if ([pages_ count] != 0)
1617 [[pages_ lastObject] setPageActive:NO];
1619 [navbar_ disableAnimation];
1621 for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
1622 [navbar_ popNavigationItem];
1625 [self pushPage:page];
1626 [navbar_ enableAnimation];
1629 - (void) pushPage:(RVPage *)page {
1630 if ([pages_ count] != 0)
1631 [[pages_ lastObject] setPageActive:NO];
1633 NSString *title = [page title]; {
1634 const char *data = [title UTF8String];
1635 size_t size = [title length];
1637 Pcre title_r("^(.*?)( \\(.*\\))?$");
1638 if (title_r(data, size))
1642 NSString *backButtonTitle = [page backButtonTitle];
1643 if (backButtonTitle == nil)
1644 backButtonTitle = title;
1646 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1647 [navitem setBackButtonTitle:backButtonTitle];
1648 [navbar_ pushNavigationItem:navitem];
1650 BOOL animated = [pages_ count] == 0 ? NO : YES;
1651 [transition_ transition:(animated ? 1 : 0) toView:page];
1652 [page setPageActive:YES];
1654 [pages_ addObject:page];
1655 [self reloadButtonsForPage:page];
1657 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
1660 - (void) popPages:(unsigned)pages {
1664 [[pages_ lastObject] setPageActive:NO];
1667 for (unsigned i(0); i != pages; ++i)
1668 [navbar_ popNavigationItem];
1671 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1674 - (void) setPrompt:(NSString *)prompt {
1675 [navbar_ setPrompt:prompt];
1678 - (void) resetViewAnimated:(BOOL)animated {
1681 if ([pages_ count] > 1) {
1682 [navbar_ disableAnimation];
1683 while ([pages_ count] != (animated ? 2 : 1))
1684 [navbar_ popNavigationItem];
1685 [navbar_ enableAnimation];
1687 [navbar_ popNavigationItem];
1692 [self resetViewAnimated:animated toPage:[pages_ lastObject]];
1695 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
1696 [page resetViewAnimated:animated];
1697 [transition_ transition:(animated ? 2 : 0) toView:page];
1698 [page setPageActive:YES];
1699 [self reloadButtonsForPage:page];
1700 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
1703 - (void) setTitle:(NSString *)title forPage:(RVPage *)page {
1704 if ([pages_ count] == 0 || page != [pages_ lastObject])
1706 UINavigationItem *navitem = [navbar_ topItem];
1707 [navitem setTitle:title];
1710 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
1711 if ([pages_ count] == 0 || page != [pages_ lastObject])
1713 UINavigationItem *navitem = [navbar_ topItem];
1714 [navitem setBackButtonTitle:title];
1717 - (void) reloadButtonsForPage:(RVPage *)page {
1718 if ([pages_ count] == 0 || page != [pages_ lastObject])
1720 NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
1721 [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
1724 - (void) reloadData {
1725 for (int i(0), e([pages_ count]); i != e; ++i) {
1726 RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
1731 - (CGRect) pageBounds {
1732 return [transition_ bounds];
1737 /* RVPage Implementation {{{ */
1738 @implementation RVPage
1740 - (NSString *) title {
1741 [self doesNotRecognizeSelector:_cmd];
1745 - (NSString *) backButtonTitle {
1749 - (NSString *) leftButtonTitle {
1753 - (NSString *) rightButtonTitle {
1757 - (void) _rightButtonClicked {
1758 [self doesNotRecognizeSelector:_cmd];
1761 - (void) _leftButtonClicked {
1762 [self doesNotRecognizeSelector:_cmd];
1765 - (UIView *) accessoryView {
1769 - (void) setPageActive:(BOOL)active {
1772 - (void) resetViewAnimated:(BOOL)animated {
1773 [self doesNotRecognizeSelector:_cmd];
1776 - (void) setTitle:(NSString *)title {
1777 [book_ setTitle:title forPage:self];
1780 - (void) setBackButtonTitle:(NSString *)title {
1781 [book_ setBackButtonTitle:title forPage:self];
1784 - (void) reloadButtons {
1785 [book_ reloadButtonsForPage:self];
1788 - (void) reloadData {
1791 - (id) initWithBook:(RVBook *)book {
1792 if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
1797 - (void) setDelegate:(id)delegate {
1798 delegate_ = delegate;
1804 /* Confirmation View {{{ */
1805 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1806 if ([packages count] == 0)
1809 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1810 [fields setObject:text forKey:key];
1812 CGColor blue(space_, 0, 0, 0.4, 1);
1813 [text setTextColor:blue];
1816 @protocol ConfirmationViewDelegate
1821 @interface ConfirmationView : UIView {
1822 Database *database_;
1824 UITransitionView *transition_;
1826 UINavigationBar *navbar_;
1827 UIPreferencesTable *table_;
1828 NSMutableDictionary *fields_;
1829 UIAlertSheet *essential_;
1834 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1838 @implementation ConfirmationView
1841 [navbar_ setDelegate:nil];
1842 [transition_ setDelegate:nil];
1843 [table_ setDataSource:nil];
1845 [transition_ release];
1850 if (essential_ != nil)
1851 [essential_ release];
1856 [transition_ transition:7 toView:nil];
1860 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1861 if (from != nil && to == nil)
1862 [self removeFromSuperview];
1865 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1868 if (essential_ != nil)
1869 [essential_ popupAlertAnimated:YES];
1871 [delegate_ confirm];
1880 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1881 [essential_ dismiss];
1885 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1889 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1891 case 0: return @"Statistics";
1892 case 1: return @"Modifications";
1894 default: _assert(false);
1898 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1901 case 1: return [fields_ count];
1903 default: _assert(false);
1907 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1908 if (group != 1 || row == -1)
1911 _assert(size_t(row) < [fields_ count]);
1912 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1916 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1917 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1918 [cell setShowSelection:NO];
1921 case 0: switch (row) {
1923 [cell setTitle:@"Downloading"];
1924 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1928 [cell setTitle:@"Resuming At"];
1929 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1933 double size([database_ cache]->UsrSize());
1936 [cell setTitle:@"Disk Freeing"];
1937 [cell setValue:SizeString(-size)];
1939 [cell setTitle:@"Disk Using"];
1940 [cell setValue:SizeString(size)];
1944 default: _assert(false);
1948 _assert(size_t(row) < [fields_ count]);
1949 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1950 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1953 default: _assert(false);
1959 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1960 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1961 database_ = database;
1962 delegate_ = delegate;
1964 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1965 [self addSubview:transition_];
1967 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1969 CGSize navsize = [UINavigationBar defaultSize];
1970 CGRect navrect = {{0, 0}, navsize};
1971 CGRect bounds = [overlay_ bounds];
1973 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1974 [navbar_ setBarStyle:1];
1975 [navbar_ setDelegate:self];
1977 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1978 [navbar_ pushNavigationItem:navitem];
1979 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1981 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1983 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1984 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1985 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1986 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1987 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
1991 pkgCacheFile &cache([database_ cache]);
1992 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
1993 Package *package([Package packageWithIterator:iterator database:database_]);
1994 NSString *name([package name]);
1995 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
1996 pkgDepCache::StateCache &state(cache[iterator]);
1998 if (state.NewInstall())
1999 [installing addObject:name];
2000 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2001 [reinstalling addObject:name];
2002 else if (state.Upgrade())
2003 [upgrading addObject:name];
2004 else if (state.Downgrade())
2005 [downgrading addObject:name];
2006 else if (state.Delete()) {
2009 [removing addObject:name];
2016 essential_ = [[UIAlertSheet alloc]
2017 initWithTitle:@"Unable to Comply"
2018 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2019 defaultButtonIndex:0
2024 [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."];
2027 AddTextView(fields_, installing, @"Installing");
2028 AddTextView(fields_, reinstalling, @"Reinstalling");
2029 AddTextView(fields_, upgrading, @"Upgrading");
2030 AddTextView(fields_, downgrading, @"Downgrading");
2031 AddTextView(fields_, removing, @"Removing");
2033 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2034 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2037 [table_ setReusesTableCells:YES];
2038 [table_ setDataSource:self];
2039 [table_ reloadData];
2041 [overlay_ addSubview:navbar_];
2042 [overlay_ addSubview:table_];
2044 [view addSubview:self];
2046 [transition_ setDelegate:self];
2048 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2049 [transition_ transition:0 toView:blank];
2050 [transition_ transition:3 toView:overlay_];
2057 /* Progress Data {{{ */
2058 @interface ProgressData : NSObject {
2064 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2071 @implementation ProgressData
2073 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2074 if ((self = [super init]) != nil) {
2075 selector_ = selector;
2095 /* Progress View {{{ */
2096 @interface ProgressView : UIView <
2100 UIView *background_;
2101 UITransitionView *transition_;
2103 UINavigationBar *navbar_;
2104 UIProgressBar *progress_;
2105 UITextView *output_;
2106 UITextLabel *status_;
2110 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2112 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
2113 - (void) setContentView:(UIView *)view;
2116 - (void) _retachThread;
2117 - (void) _detachNewThreadData:(ProgressData *)data;
2118 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2122 @protocol ProgressViewDelegate
2123 - (void) progressViewIsComplete:(ProgressView *)sender;
2126 @implementation ProgressView
2129 [transition_ setDelegate:nil];
2130 [navbar_ setDelegate:nil];
2133 if (background_ != nil)
2134 [background_ release];
2135 [transition_ release];
2138 [progress_ release];
2144 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2145 if (bootstrap_ && from == overlay_ && to == view_)
2149 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
2150 if ((self = [super initWithFrame:frame]) != nil) {
2151 delegate_ = delegate;
2153 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2154 [transition_ setDelegate:self];
2156 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2159 [overlay_ setBackgroundColor:Black_];
2161 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2162 [background_ setBackgroundColor:Black_];
2163 [self addSubview:background_];
2166 [self addSubview:transition_];
2168 CGSize navsize = [UINavigationBar defaultSize];
2169 CGRect navrect = {{0, 0}, navsize};
2171 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2172 [overlay_ addSubview:navbar_];
2174 [navbar_ setBarStyle:1];
2175 [navbar_ setDelegate:self];
2177 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2178 [navbar_ pushNavigationItem:navitem];
2180 CGRect bounds = [overlay_ bounds];
2181 CGSize prgsize = [UIProgressBar defaultSize];
2184 (bounds.size.width - prgsize.width) / 2,
2185 bounds.size.height - prgsize.height - 20
2188 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2189 [overlay_ addSubview:progress_];
2191 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2193 bounds.size.height - prgsize.height - 50,
2194 bounds.size.width - 20,
2198 [status_ setColor:White_];
2199 [status_ setBackgroundColor:Clear_];
2201 [status_ setCentersHorizontally:YES];
2202 //[status_ setFont:font];
2204 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2206 navrect.size.height + 20,
2207 bounds.size.width - 20,
2208 bounds.size.height - navsize.height - 62 - navrect.size.height
2211 //[output_ setTextFont:@"Courier New"];
2212 [output_ setTextSize:12];
2214 [output_ setTextColor:White_];
2215 [output_ setBackgroundColor:Clear_];
2217 [output_ setMarginTop:0];
2218 [output_ setAllowsRubberBanding:YES];
2220 [overlay_ addSubview:output_];
2221 [overlay_ addSubview:status_];
2223 [progress_ setStyle:0];
2227 - (void) setContentView:(UIView *)view {
2228 view_ = [view retain];
2231 - (void) resetView {
2232 [transition_ transition:6 toView:view_];
2235 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2239 - (void) _retachThread {
2240 [delegate_ progressViewIsComplete:self];
2244 - (void) _detachNewThreadData:(ProgressData *)data {
2245 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2247 [[data target] performSelector:[data selector] withObject:[data object]];
2250 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2255 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2256 [navbar_ popNavigationItem];
2257 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
2258 [navbar_ pushNavigationItem:navitem];
2260 [status_ setText:nil];
2261 [output_ setText:@""];
2262 [progress_ setProgress:0];
2264 [transition_ transition:6 toView:overlay_];
2267 detachNewThreadSelector:@selector(_detachNewThreadData:)
2269 withObject:[[ProgressData alloc]
2270 initWithSelector:selector
2277 - (void) setProgressError:(NSString *)error {
2279 performSelectorOnMainThread:@selector(_setProgressError:)
2285 - (void) setProgressTitle:(NSString *)title {
2287 performSelectorOnMainThread:@selector(_setProgressTitle:)
2293 - (void) setProgressPercent:(float)percent {
2295 performSelectorOnMainThread:@selector(_setProgressPercent:)
2296 withObject:[NSNumber numberWithFloat:percent]
2301 - (void) addProgressOutput:(NSString *)output {
2303 performSelectorOnMainThread:@selector(_addProgressOutput:)
2309 - (void) _setProgressError:(NSString *)error {
2310 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2311 initWithTitle:@"Package Error"
2312 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2313 defaultButtonIndex:0
2318 [sheet setBodyText:error];
2319 [sheet popupAlertAnimated:YES];
2322 - (void) _setProgressTitle:(NSString *)title {
2323 [status_ setText:[title stringByAppendingString:@"..."]];
2326 - (void) _setProgressPercent:(NSNumber *)percent {
2327 [progress_ setProgress:[percent floatValue]];
2330 - (void) _addProgressOutput:(NSString *)output {
2331 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2332 CGSize size = [output_ contentSize];
2333 CGRect rect = {{0, size.height}, {size.width, 0}};
2334 [output_ scrollRectToVisible:rect animated:YES];
2340 /* Package Cell {{{ */
2341 @interface PackageCell : UITableCell {
2344 UITextLabel *description_;
2345 UITextLabel *source_;
2346 UIImageView *trusted_;
2349 - (PackageCell *) init;
2350 - (void) setPackage:(Package *)package;
2352 - (void) _setSelected:(float)fraction;
2353 - (void) setSelected:(BOOL)selected;
2354 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2355 - (void) _setSelectionFadeFraction:(float)fraction;
2359 @implementation PackageCell
2364 [description_ release];
2370 - (PackageCell *) init {
2371 if ((self = [super init]) != nil) {
2372 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2373 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2374 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2376 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2378 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2379 [name_ setBackgroundColor:Clear_];
2380 [name_ setFont:bold];
2382 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2383 [source_ setBackgroundColor:Clear_];
2384 [source_ setFont:large];
2386 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2387 [description_ setBackgroundColor:Clear_];
2388 [description_ setFont:small];
2390 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2391 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
2393 [self addSubview:icon_];
2394 [self addSubview:name_];
2395 [self addSubview:description_];
2396 [self addSubview:source_];
2404 - (void) setPackage:(Package *)package {
2405 Source *source = [package source];
2407 UIImage *image = nil;
2408 if (NSString *icon = [package icon])
2409 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2410 if (image == nil) if (NSString *icon = [source defaultIcon])
2411 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2413 image = [UIImage applicationImageNamed:@"unknown.png"];
2414 [icon_ setImage:image];
2417 CGSize size = [image size];
2418 float scale = 30 / std::max(size.width, size.height);
2419 [icon_ zoomToScale:scale];
2422 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2424 [name_ setText:[package name]];
2425 [description_ setText:[package tagline]];
2430 if (source != nil) {
2431 label = [source label];
2432 trusted = [source trusted];
2433 } else if ([[package id] isEqualToString:@"firmware"]) {
2437 label = @"Unknown/Local";
2441 [source_ setText:[NSString stringWithFormat:@"from %@", label]];
2444 [self addSubview:trusted_];
2446 [trusted_ removeFromSuperview];
2449 - (void) _setSelected:(float)fraction {
2450 CGColor black(space_,
2451 Interpolate(0.0, 1.0, fraction),
2452 Interpolate(0.0, 1.0, fraction),
2453 Interpolate(0.0, 1.0, fraction),
2456 CGColor gray(space_,
2457 Interpolate(0.4, 1.0, fraction),
2458 Interpolate(0.4, 1.0, fraction),
2459 Interpolate(0.4, 1.0, fraction),
2462 [name_ setColor:black];
2463 [description_ setColor:gray];
2464 [source_ setColor:black];
2467 - (void) setSelected:(BOOL)selected {
2468 [self _setSelected:(selected ? 1.0 : 0.0)];
2469 [super setSelected:selected];
2472 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2474 [self _setSelected:(selected ? 1.0 : 0.0)];
2475 [super setSelected:selected withFade:fade];
2478 - (void) _setSelectionFadeFraction:(float)fraction {
2479 [self _setSelected:fraction];
2480 [super _setSelectionFadeFraction:fraction];
2485 /* Section Cell {{{ */
2486 @interface SectionCell : UITableCell {
2488 UITextLabel *count_;
2492 - (void) setSection:(Section *)section;
2494 - (void) _setSelected:(float)fraction;
2495 - (void) setSelected:(BOOL)selected;
2496 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2497 - (void) _setSelectionFadeFraction:(float)fraction;
2501 @implementation SectionCell
2510 if ((self = [super init]) != nil) {
2511 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2512 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2514 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2515 [name_ setBackgroundColor:Clear_];
2516 [name_ setFont:bold];
2518 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2519 [count_ setCentersHorizontally:YES];
2520 [count_ setBackgroundColor:Clear_];
2521 [count_ setFont:small];
2522 [count_ setColor:White_];
2524 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2525 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2527 [self addSubview:folder];
2528 [self addSubview:name_];
2529 [self addSubview:count_];
2531 [self _setSelected:0];
2538 - (void) setSection:(Section *)section {
2539 if (section == nil) {
2540 [name_ setText:@"All Packages"];
2541 [count_ setText:nil];
2543 NSString *name = [section name];
2544 [name_ setText:(name == nil ? @"(No Section)" : name)];
2545 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2549 - (void) _setSelected:(float)fraction {
2550 CGColor black(space_,
2551 Interpolate(0.0, 1.0, fraction),
2552 Interpolate(0.0, 1.0, fraction),
2553 Interpolate(0.0, 1.0, fraction),
2556 [name_ setColor:black];
2559 - (void) setSelected:(BOOL)selected {
2560 [self _setSelected:(selected ? 1.0 : 0.0)];
2561 [super setSelected:selected];
2564 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2566 [self _setSelected:(selected ? 1.0 : 0.0)];
2567 [super setSelected:selected withFade:fade];
2570 - (void) _setSelectionFadeFraction:(float)fraction {
2571 [self _setSelected:fraction];
2572 [super _setSelectionFadeFraction:fraction];
2578 /* Browser Interface {{{ */
2579 @interface BrowserView : RVPage {
2580 _transient Database *database_;
2581 UIScroller *scroller_;
2582 UIWebView *webview_;
2583 NSMutableArray *urls_;
2584 UIProgressIndicator *indicator_;
2590 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2591 - (void) loadURL:(NSURL *)url;
2593 - (void) loadRequest:(NSURLRequest *)request;
2596 - (WebView *) webView;
2598 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2603 /* Package View {{{ */
2604 @protocol PackageViewDelegate
2605 - (void) performPackage:(Package *)package;
2608 @interface PackageView : RVPage {
2609 _transient Database *database_;
2610 UIPreferencesTable *table_;
2613 UITextView *description_;
2616 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2617 - (void) setPackage:(Package *)package;
2621 @implementation PackageView
2624 [table_ setDataSource:nil];
2625 [table_ setDelegate:nil];
2627 if (package_ != nil)
2631 if (description_ != nil)
2632 [description_ release];
2637 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2638 return [package_ source] == nil ? 2 : 3;
2641 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2644 case 1: return @"Package Details";
2645 case 2: return @"Source Information";
2647 default: _assert(false);
2651 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2652 if (group != 0 || row != 1)
2655 return [description_ visibleTextRect].size.height + TextViewOffset_;
2658 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2660 case 0: return [package_ website] == nil ? 2 : 3;
2664 default: _assert(false);
2668 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2669 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2670 [cell setShowSelection:NO];
2673 case 0: switch (row) {
2675 [cell setTitle:[package_ name]];
2676 [cell setValue:[package_ latest]];
2680 [cell addSubview:description_];
2684 [cell setTitle:@"More Information"];
2685 [cell setShowDisclosure:YES];
2686 [cell setShowSelection:YES];
2689 default: _assert(false);
2692 case 1: switch (row) {
2694 [cell setTitle:@"Identifier"];
2695 [cell setValue:[package_ id]];
2699 [cell setTitle:@"Installed Version"];
2700 NSString *installed([package_ installed]);
2701 [cell setValue:(installed == nil ? @"n/a" : installed)];
2705 [cell setTitle:@"Section"];
2706 NSString *section([package_ section]);
2707 [cell setValue:(section == nil ? @"n/a" : section)];
2711 [cell setTitle:@"Expanded Size"];
2712 [cell setValue:SizeString([package_ size])];
2716 [cell setTitle:@"Maintainer"];
2717 [cell setValue:[[package_ maintainer] name]];
2718 [cell setShowDisclosure:YES];
2719 [cell setShowSelection:YES];
2722 default: _assert(false);
2725 case 2: switch (row) {
2727 [cell setTitle:[[package_ source] label]];
2728 [cell setValue:[[package_ source] version]];
2732 [cell setValue:[[package_ source] description]];
2736 [cell setTitle:@"Origin"];
2737 [cell setValue:[[package_ source] origin]];
2740 default: _assert(false);
2743 default: _assert(false);
2749 - (BOOL) canSelectRow:(int)row {
2753 - (void) tableRowSelected:(NSNotification *)notification {
2754 int row = [table_ selectedRow];
2755 NSString *website = [package_ website];
2757 if (row == (website == nil ? 8 : 9))
2758 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2759 [[package_ maintainer] email],
2760 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2762 else if (website != nil && row == 3) {
2763 NSURL *url = [NSURL URLWithString:website];
2764 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2765 [browser setDelegate:delegate_];
2766 [book_ pushPage:browser];
2767 [browser loadURL:url];
2771 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2773 case 1: [delegate_ installPackage:package_]; break;
2774 case 2: [delegate_ removePackage:package_]; break;
2780 - (void) _rightButtonClicked {
2781 if ([package_ installed] == nil)
2782 [delegate_ installPackage:package_];
2784 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2786 if ([package_ upgradable])
2787 [buttons addObject:@"Upgrade"];
2789 [buttons addObject:@"Reinstall"];
2791 [buttons addObject:@"Remove"];
2792 [buttons addObject:@"Cancel"];
2794 [delegate_ slideUp:[[[UIAlertSheet alloc]
2795 initWithTitle:@"Manage Package"
2797 defaultButtonIndex:2
2804 - (NSString *) rightButtonTitle {
2805 _assert(package_ != nil);
2806 return [package_ installed] == nil ? @"Install" : @"Manage";
2809 - (NSString *) title {
2813 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2814 if ((self = [super initWithBook:book]) != nil) {
2815 database_ = database;
2817 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2818 [self addSubview:table_];
2820 [table_ setDataSource:self];
2821 [table_ setDelegate:self];
2825 - (void) setPackage:(Package *)package {
2826 if (package_ != nil) {
2827 [package_ autorelease];
2836 if (description_ != nil) {
2837 [description_ release];
2841 if (package != nil) {
2842 package_ = [package retain];
2843 name_ = [[package id] retain];
2845 NSString *description([package description]);
2846 if (description == nil)
2847 description = [package tagline];
2848 description_ = [GetTextView(description, 12, true) retain];
2850 [description_ setTextColor:Black_];
2852 [table_ reloadData];
2856 - (void) resetViewAnimated:(BOOL)animated {
2857 [table_ resetViewAnimated:animated];
2860 - (void) reloadData {
2861 [self setPackage:[database_ packageWithName:name_]];
2862 [self reloadButtons];
2867 /* Package Table {{{ */
2868 @interface PackageTable : RVPage {
2869 _transient Database *database_;
2873 NSMutableArray *packages_;
2874 NSMutableArray *sections_;
2875 UISectionList *list_;
2878 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2880 - (void) setDelegate:(id)delegate;
2881 - (void) setObject:(id)object;
2883 - (void) reloadData;
2884 - (void) resetCursor;
2888 @implementation PackageTable
2891 [list_ setDataSource:nil];
2896 [packages_ release];
2897 [sections_ release];
2902 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2903 return [sections_ count];
2906 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2907 return [[sections_ objectAtIndex:section] name];
2910 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2911 return [[sections_ objectAtIndex:section] row];
2914 - (int) numberOfRowsInTable:(UITable *)table {
2915 return [packages_ count];
2918 - (float) table:(UITable *)table heightForRow:(int)row {
2922 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2924 reusing = [[[PackageCell alloc] init] autorelease];
2925 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2929 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2933 - (void) tableRowSelected:(NSNotification *)notification {
2934 int row = [[notification object] selectedRow];
2938 Package *package = [packages_ objectAtIndex:row];
2939 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2940 [view setDelegate:delegate_];
2941 [view setPackage:package];
2942 [book_ pushPage:view];
2945 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2946 if ((self = [super initWithBook:book]) != nil) {
2947 database_ = database;
2948 title_ = [title retain];
2950 object_ = object == nil ? nil : [object retain];
2952 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2953 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2955 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2956 [list_ setDataSource:self];
2958 UITableColumn *column = [[[UITableColumn alloc]
2959 initWithTitle:@"Name"
2961 width:[self frame].size.width
2964 UITable *table = [list_ table];
2965 [table setSeparatorStyle:1];
2966 [table addTableColumn:column];
2967 [table setDelegate:self];
2968 [table setReusesTableCells:YES];
2970 [self addSubview:list_];
2975 - (void) setDelegate:(id)delegate {
2976 delegate_ = delegate;
2979 - (void) setObject:(id)object {
2985 object_ = [object retain];
2988 - (void) reloadData {
2989 NSArray *packages = [database_ packages];
2991 [packages_ removeAllObjects];
2992 [sections_ removeAllObjects];
2994 for (size_t i(0); i != [packages count]; ++i) {
2995 Package *package([packages objectAtIndex:i]);
2996 if ([[package performSelector:filter_ withObject:object_] boolValue])
2997 [packages_ addObject:package];
3000 [packages_ sortUsingSelector:@selector(compareByName:)];
3002 Section *section = nil;
3004 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3005 Package *package = [packages_ objectAtIndex:offset];
3006 NSString *name = [package index];
3008 if (section == nil || ![[section name] isEqualToString:name]) {
3009 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3010 [sections_ addObject:section];
3013 [section addToCount];
3019 - (NSString *) title {
3023 - (void) resetViewAnimated:(BOOL)animated {
3024 [list_ resetViewAnimated:animated];
3027 - (void) resetCursor {
3028 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3034 /* Browser Implementation {{{ */
3035 @implementation BrowserView
3038 WebView *webview = [webview_ webView];
3039 [webview setFrameLoadDelegate:nil];
3040 [webview setResourceLoadDelegate:nil];
3041 [webview setUIDelegate:nil];
3043 [scroller_ setDelegate:nil];
3044 [webview_ setDelegate:nil];
3046 [scroller_ release];
3049 [indicator_ release];
3055 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3056 NSMutableURLRequest *request = [NSMutableURLRequest
3059 timeoutInterval:30.0
3062 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3063 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3064 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3066 [self loadRequest:request];
3070 - (void) loadURL:(NSURL *)url {
3071 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3074 // XXX: this needs to add the headers
3075 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3079 - (void) loadRequest:(NSURLRequest *)request {
3080 [webview_ loadRequest:request];
3083 - (void) reloadURL {
3084 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3085 [urls_ removeLastObject];
3086 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3089 - (WebView *) webView {
3090 return [webview_ webView];
3093 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3094 [scroller_ setContentSize:frame.size];
3097 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3098 [self view:sender didSetFrame:frame];
3101 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3102 return [self _addHeadersToRequest:request];
3105 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3106 [self setBackButtonTitle:title_];
3107 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3108 [browser setDelegate:delegate_];
3109 [book_ pushPage:browser];
3110 [browser loadRequest:[self _addHeadersToRequest:request]];
3111 return [browser webView];
3114 - (void) webView:(WebView *)sender willClickElement:(id)element {
3115 if (![element respondsToSelector:@selector(href)])
3117 NSString *href = [element href];
3120 if ([href hasPrefix:@"apptapp://package/"]) {
3121 NSString *name = [href substringFromIndex:18];
3122 Package *package = [database_ packageWithName:name];
3123 if (package == nil) {
3124 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3125 initWithTitle:@"Cannot Locate Package"
3126 buttons:[NSArray arrayWithObjects:@"Close", nil]
3127 defaultButtonIndex:0
3132 [sheet setBodyText:[NSString stringWithFormat:
3133 @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
3136 [sheet popupAlertAnimated:YES];
3138 [self setBackButtonTitle:title_];
3139 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3140 [view setDelegate:delegate_];
3141 [view setPackage:package];
3142 [book_ pushPage:view];
3147 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3148 title_ = [title retain];
3149 [self setTitle:title];
3152 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3153 if ([frame parentFrame] != nil)
3157 [indicator_ startAnimation];
3158 [self reloadButtons];
3160 if (title_ != nil) {
3165 [self setTitle:@"Loading..."];
3167 WebView *webview = [webview_ webView];
3168 NSString *href = [webview mainFrameURL];
3169 [urls_ addObject:[NSURL URLWithString:href]];
3171 CGRect webrect = [scroller_ frame];
3172 webrect.size.height = 0;
3173 [webview_ setFrame:webrect];
3176 - (void) _finishLoading {
3178 [indicator_ stopAnimation];
3179 [self reloadButtons];
3182 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3183 if ([frame parentFrame] != nil)
3185 [self _finishLoading];
3188 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3189 if ([frame parentFrame] != nil)
3191 [self setTitle:[error localizedDescription]];
3192 [self _finishLoading];
3195 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3196 if ((self = [super initWithBook:book]) != nil) {
3197 database_ = database;
3200 struct CGRect bounds = [self bounds];
3202 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3203 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3204 [self addSubview:pinstripe];
3206 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3207 [self addSubview:scroller_];
3209 [scroller_ setScrollingEnabled:YES];
3210 [scroller_ setAdjustForContentSizeChange:YES];
3211 [scroller_ setClipsSubviews:YES];
3212 [scroller_ setAllowsRubberBanding:YES];
3213 [scroller_ setScrollDecelerationFactor:0.99];
3214 [scroller_ setDelegate:self];
3216 CGRect webrect = [scroller_ bounds];
3217 webrect.size.height = 0;
3219 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3220 [scroller_ addSubview:webview_];
3222 [webview_ setTilingEnabled:YES];
3223 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3224 [webview_ setAutoresizes:YES];
3225 [webview_ setDelegate:self];
3226 //[webview_ setEnabledGestures:2];
3228 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3229 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3230 [indicator_ setStyle:0];
3232 Package *package([database_ packageWithName:@"cydia"]);
3233 NSString *application = package == nil ? @"Cydia" : [NSString
3234 stringWithFormat:@"Cydia/%@",
3238 WebView *webview = [webview_ webView];
3239 [webview setApplicationNameForUserAgent:application];
3240 [webview setFrameLoadDelegate:self];
3241 [webview setResourceLoadDelegate:self];
3242 [webview setUIDelegate:self];
3244 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3248 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3252 - (void) _leftButtonClicked {
3253 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3254 initWithTitle:@"About Cydia Packager"
3255 buttons:[NSArray arrayWithObjects:@"Close", nil]
3256 defaultButtonIndex:0
3262 @"Copyright (C) 2008\n"
3263 "Jay Freeman (saurik)\n"
3264 "saurik@saurik.com\n"
3265 "http://www.saurik.com/\n"
3268 "http://www.theokorigroup.com/\n"
3270 "College of Creative Studies,\n"
3271 "University of California,\n"
3273 "http://www.ccs.ucsb.edu/"
3276 [sheet popupAlertAnimated:YES];
3279 - (void) _rightButtonClicked {
3283 - (NSString *) leftButtonTitle {
3287 - (NSString *) rightButtonTitle {
3288 return loading_ ? @"" : @"Reload";
3291 - (NSString *) title {
3295 - (NSString *) backButtonTitle {
3299 - (void) setPageActive:(BOOL)active {
3301 [book_ addSubview:indicator_];
3303 [indicator_ removeFromSuperview];
3306 - (void) resetViewAnimated:(BOOL)animated {
3312 @interface CYBook : RVBook <
3315 _transient Database *database_;
3317 UIProgressIndicator *indicator_;
3318 UITextLabel *prompt_;
3319 UIProgressBar *progress_;
3323 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3329 /* Install View {{{ */
3330 @interface InstallView : RVPage {
3331 _transient Database *database_;
3332 NSMutableArray *packages_;
3333 NSMutableArray *sections_;
3337 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3338 - (void) reloadData;
3342 @implementation InstallView
3345 [list_ setDataSource:nil];
3346 [list_ setDelegate:nil];
3348 [packages_ release];
3349 [sections_ release];
3354 - (int) numberOfRowsInTable:(UITable *)table {
3355 return [sections_ count] + 1;
3358 - (float) table:(UITable *)table heightForRow:(int)row {
3362 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3364 reusing = [[[SectionCell alloc] init] autorelease];
3365 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3369 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3373 - (void) tableRowSelected:(NSNotification *)notification {
3374 int row = [[notification object] selectedRow];
3383 title = @"All Packages";
3385 section = [sections_ objectAtIndex:(row - 1)];
3386 title = [section name];
3389 PackageTable *table = [[[PackageTable alloc]
3393 filter:@selector(isUninstalledInSection:)
3394 with:(section == nil ? nil : [section name])
3397 [table setDelegate:delegate_];
3399 [book_ pushPage:table];
3402 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3403 if ((self = [super initWithBook:book]) != nil) {
3404 database_ = database;
3406 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3407 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3409 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3410 [self addSubview:list_];
3412 UITableColumn *column = [[[UITableColumn alloc]
3413 initWithTitle:@"Name"
3415 width:[self frame].size.width
3418 [list_ setDataSource:self];
3419 [list_ setSeparatorStyle:1];
3420 [list_ addTableColumn:column];
3421 [list_ setDelegate:self];
3422 [list_ setReusesTableCells:YES];
3428 - (void) reloadData {
3429 NSArray *packages = [database_ packages];
3431 [packages_ removeAllObjects];
3432 [sections_ removeAllObjects];
3434 for (size_t i(0); i != [packages count]; ++i) {
3435 Package *package([packages objectAtIndex:i]);
3436 if ([package installed] == nil)
3437 [packages_ addObject:package];
3440 [packages_ sortUsingSelector:@selector(compareBySection:)];
3442 Section *section = nil;
3443 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3444 Package *package = [packages_ objectAtIndex:offset];
3445 NSString *name = [package section];
3447 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3448 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3449 [sections_ addObject:section];
3452 [section addToCount];
3458 - (void) resetViewAnimated:(BOOL)animated {
3459 [list_ resetViewAnimated:animated];
3462 - (NSString *) title {
3466 - (NSString *) backButtonTitle {
3472 /* Changes View {{{ */
3473 @interface ChangesView : RVPage {
3474 _transient Database *database_;
3475 NSMutableArray *packages_;
3476 NSMutableArray *sections_;
3477 UISectionList *list_;
3481 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3482 - (void) reloadData;
3486 @implementation ChangesView
3489 [[list_ table] setDelegate:nil];
3490 [list_ setDataSource:nil];
3492 [packages_ release];
3493 [sections_ release];
3498 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3499 return [sections_ count];
3502 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3503 return [[sections_ objectAtIndex:section] name];
3506 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3507 return [[sections_ objectAtIndex:section] row];
3510 - (int) numberOfRowsInTable:(UITable *)table {
3511 return [packages_ count];
3514 - (float) table:(UITable *)table heightForRow:(int)row {
3518 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3520 reusing = [[[PackageCell alloc] init] autorelease];
3521 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3525 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3529 - (void) tableRowSelected:(NSNotification *)notification {
3530 int row = [[notification object] selectedRow];
3533 Package *package = [packages_ objectAtIndex:row];
3534 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3535 [view setDelegate:delegate_];
3536 [view setPackage:package];
3537 [book_ pushPage:view];
3540 - (void) _leftButtonClicked {
3541 [(CYBook *)book_ update];
3542 [self reloadButtons];
3545 - (void) _rightButtonClicked {
3546 [delegate_ distUpgrade];
3549 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3550 if ((self = [super initWithBook:book]) != nil) {
3551 database_ = database;
3553 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3554 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3556 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3557 [self addSubview:list_];
3559 [list_ setShouldHideHeaderInShortLists:NO];
3560 [list_ setDataSource:self];
3561 //[list_ setSectionListStyle:1];
3563 UITableColumn *column = [[[UITableColumn alloc]
3564 initWithTitle:@"Name"
3566 width:[self frame].size.width
3569 UITable *table = [list_ table];
3570 [table setSeparatorStyle:1];
3571 [table addTableColumn:column];
3572 [table setDelegate:self];
3573 [table setReusesTableCells:YES];
3579 - (void) reloadData {
3580 NSArray *packages = [database_ packages];
3582 [packages_ removeAllObjects];
3583 [sections_ removeAllObjects];
3585 for (size_t i(0); i != [packages count]; ++i) {
3586 Package *package([packages objectAtIndex:i]);
3587 if ([package installed] == nil || [package upgradable])
3588 [packages_ addObject:package];
3591 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3593 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3594 Section *section = nil;
3597 bool unseens = false;
3599 CFLocaleRef locale = CFLocaleCopyCurrent();
3600 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3602 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3603 Package *package = [packages_ objectAtIndex:offset];
3605 if ([package upgradable]) {
3607 [upgradable addToCount];
3610 NSDate *seen = [package seen];
3615 name = [@"n/a ?" retain];
3617 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3620 if (section == nil || ![[section name] isEqualToString:name]) {
3621 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3622 [sections_ addObject:section];
3626 [section addToCount];
3630 CFRelease(formatter);
3634 Section *last = [sections_ lastObject];
3635 size_t count = [last count];
3636 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3637 [sections_ removeLastObject];
3641 [sections_ insertObject:upgradable atIndex:0];
3644 [self reloadButtons];
3647 - (void) resetViewAnimated:(BOOL)animated {
3648 [list_ resetViewAnimated:animated];
3651 - (NSString *) leftButtonTitle {
3652 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3655 - (NSString *) rightButtonTitle {
3656 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3659 - (NSString *) title {
3665 /* Search View {{{ */
3666 @protocol SearchViewDelegate
3667 - (void) showKeyboard:(BOOL)show;
3670 @interface SearchView : RVPage {
3672 UISearchField *field_;
3673 UITransitionView *transition_;
3674 PackageTable *table_;
3675 UIPreferencesTable *advanced_;
3680 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3681 - (void) reloadData;
3685 @implementation SearchView
3689 [[field_ textTraits] setEditingDelegate:nil];
3691 [field_ setDelegate:nil];
3693 [accessory_ release];
3695 [transition_ release];
3697 [advanced_ release];
3702 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3703 [delegate_ showKeyboard:YES];
3704 [table_ setEnabled:NO];
3705 [self addSubview:dimmed_];
3708 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3709 [dimmed_ removeFromSuperview];
3710 [table_ setEnabled:YES];
3711 [delegate_ showKeyboard:NO];
3714 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3715 NSString *text([field_ text]);
3716 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3719 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3720 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3724 [field_ resignFirstResponder];
3728 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3729 if ((self = [super initWithBook:book]) != nil) {
3730 CGRect pageBounds = [book_ pageBounds];
3732 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
3733 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3734 [self addSubview:pinstripe];
3736 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3737 CGColor dimmed(space_, 0, 0, 0, 0.5);
3738 [dimmed_ setBackgroundColor:dimmed];
3740 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3741 [self addSubview:transition_];
3743 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3745 table_ = [[PackageTable alloc]
3749 filter:@selector(isSearchedForBy:)
3753 [transition_ transition:0 toView:table_];
3755 CGRect cnfrect = {{3, 36}, {17, 18}};
3758 area.origin.x = cnfrect.size.width + 12;
3760 area.size.width = [self bounds].size.width - area.origin.x - 18;
3761 area.size.height = [UISearchField defaultHeight];
3763 field_ = [[UISearchField alloc] initWithFrame:area];
3765 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3766 [field_ setFont:font];
3769 [field_ setPlaceholder:@"Package Names & Descriptions"];
3770 [field_ setPaddingTop:5];
3771 [field_ setDelegate:self];
3774 UITextTraits *traits = [field_ textTraits];
3775 [traits setEditingDelegate:self];
3776 [traits setReturnKeyType:6];
3777 [traits setAutoCapsType:0];
3778 [traits setAutoCorrectionType:1];
3781 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3782 [configure setShowPressFeedback:YES];
3783 [configure setImage:[UIImage applicationImageNamed:@"configure.png"]];
3784 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3786 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3787 [accessory_ addSubview:field_];
3788 [accessory_ addSubview:configure];
3793 LKAnimation *animation = [LKTransition animation];
3794 [animation setType:@"oglFlip"];
3795 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3796 [animation setFillMode:@"extended"];
3797 [animation setTransitionFlags:3];
3798 [animation setDuration:10];
3799 [animation setSpeed:0.35];
3800 [animation setSubtype:@"fromLeft"];
3801 [[self _layer] addAnimation:animation forKey:0];
3802 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3803 flipped_ = !flipped_;
3806 - (void) configurePushed {
3807 [field_ resignFirstResponder];
3811 - (void) resetViewAnimated:(BOOL)animated {
3814 [table_ resetViewAnimated:animated];
3817 - (void) reloadData {
3820 [table_ setObject:[field_ text]];
3821 [table_ reloadData];
3822 [table_ resetCursor];
3825 - (UIView *) accessoryView {
3829 - (NSString *) title {
3833 - (NSString *) backButtonTitle {
3840 @implementation CYBook
3844 [indicator_ release];
3846 [progress_ release];
3855 [navbar_ setPrompt:@""];
3856 [navbar_ addSubview:overlay_];
3857 [indicator_ startAnimation];
3858 [prompt_ setText:@"Updating Database..."];
3859 [progress_ setProgress:0];
3864 detachNewThreadSelector:@selector(_update)
3873 [overlay_ removeFromSuperview];
3874 [indicator_ stopAnimation];
3875 [delegate_ reloadData];
3877 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3880 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3881 if ((self = [super initWithFrame:frame]) != nil) {
3882 database_ = database;
3884 CGRect ovrrect = [navbar_ bounds];
3885 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3887 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3889 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3890 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3891 CGRect indrect = {{indoffset, indoffset}, indsize};
3893 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3894 [indicator_ setStyle:2];
3895 [overlay_ addSubview:indicator_];
3897 CGSize prmsize = {200, indsize.width};
3900 indoffset * 2 + indsize.width,
3901 (ovrrect.size.height - prmsize.height) / 2
3904 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3906 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3908 [prompt_ setColor:White_];
3909 [prompt_ setBackgroundColor:Clear_];
3910 [prompt_ setFont:font];
3914 [overlay_ addSubview:prompt_];
3916 CGSize prgsize = {75, 100};
3919 ovrrect.size.width - prgsize.width - 10,
3920 (ovrrect.size.height - prgsize.height) / 2
3923 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3924 [progress_ setStyle:0];
3925 [overlay_ addSubview:progress_];
3930 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3933 status.setDelegate(self);
3935 [database_ updateWithStatus:status];
3938 performSelectorOnMainThread:@selector(_update_)
3946 - (void) setProgressError:(NSString *)error {
3948 performSelectorOnMainThread:@selector(_setProgressError:)
3954 - (void) setProgressTitle:(NSString *)title {
3956 performSelectorOnMainThread:@selector(_setProgressTitle:)
3962 - (void) setProgressPercent:(float)percent {
3965 - (void) addProgressOutput:(NSString *)output {
3967 performSelectorOnMainThread:@selector(_addProgressOutput:)
3973 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3977 - (void) _setProgressError:(NSString *)error {
3978 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
3981 - (void) _setProgressTitle:(NSString *)title {
3982 [prompt_ setText:[title stringByAppendingString:@"..."]];
3985 - (void) _addProgressOutput:(NSString *)output {
3990 @interface Cydia : UIApplication <
3991 ConfirmationViewDelegate,
3992 ProgressViewDelegate,
4001 UIButtonBar *buttonbar_;
4003 ConfirmationView *confirm_;
4005 Database *database_;
4006 ProgressView *progress_;
4010 UIKeyboard *keyboard_;
4015 @implementation Cydia
4017 - (void) _reloadData {
4018 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4019 [hud setText:@"Reloading Data"];
4020 [overlay_ addSubview:hud];
4023 [database_ reloadData];
4027 if (Packages_ == nil) {
4028 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
4029 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4034 NSArray *packages = [database_ packages];
4035 for (int i(0), e([packages count]); i != e; ++i) {
4036 Package *package = [packages objectAtIndex:i];
4037 if ([package upgradable])
4042 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4043 [buttonbar_ setBadgeValue:badge forButton:3];
4044 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4045 [buttonbar_ setBadgeAnimated:YES forButton:3];
4046 [self setApplicationBadge:badge];
4048 [buttonbar_ setBadgeValue:nil forButton:3];
4049 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4050 [buttonbar_ setBadgeAnimated:NO forButton:3];
4051 [self removeApplicationBadge];
4054 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4058 [hud removeFromSuperview];*/
4061 - (void) reloadData {
4062 @synchronized (self) {
4063 if (confirm_ == nil)
4069 pkgProblemResolver *resolver = [database_ resolver];
4071 resolver->InstallProtect();
4072 if (!resolver->Resolve(true))
4077 [database_ prepare];
4079 if ([database_ cache]->BrokenCount() == 0)
4080 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4082 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4083 NSArray *packages = [database_ packages];
4085 for (size_t i(0); i != [packages count]; ++i) {
4086 Package *package = [packages objectAtIndex:i];
4087 if ([package broken])
4088 [broken addObject:[package name]];
4091 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4092 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4093 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4094 defaultButtonIndex:0
4099 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4100 [sheet popupAlertAnimated:YES];
4106 - (void) installPackage:(Package *)package {
4107 @synchronized (self) {
4114 - (void) removePackage:(Package *)package {
4115 @synchronized (self) {
4122 - (void) distUpgrade {
4123 @synchronized (self) {
4124 [database_ upgrade];
4130 @synchronized (self) {
4138 [overlay_ removeFromSuperview];
4142 detachNewThreadSelector:@selector(perform)
4149 - (void) bootstrap_ {
4151 [database_ upgrade];
4152 [database_ prepare];
4153 [database_ perform];
4156 - (void) bootstrap {
4158 detachNewThreadSelector:@selector(bootstrap_)
4161 title:@"Bootstrap Install..."
4165 - (void) progressViewIsComplete:(ProgressView *)progress {
4166 @synchronized (self) {
4169 if (confirm_ != nil) {
4170 [underlay_ addSubview:overlay_];
4171 [confirm_ removeFromSuperview];
4178 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4182 - (void) setPage:(RVPage *)page {
4183 [page setDelegate:self];
4184 [book_ setPage:page];
4187 - (RVPage *) _setNewsPage {
4188 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4189 [self setPage:browser];
4190 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4194 - (void) buttonBarItemTapped:(id)sender {
4195 unsigned tag = [sender tag];
4199 [self _setNewsPage];
4203 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4207 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4211 [self setPage:[[[PackageTable alloc]
4215 filter:@selector(isInstalledInSection:)
4221 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4231 - (void) applicationWillSuspend {
4232 [super applicationWillSuspend];
4235 if (FW_LEAST(1,1,3))
4236 notify_post("com.apple.language.changed");
4238 system("launchctl stop com.apple.SpringBoard");
4241 - (void) applicationDidFinishLaunching:(id)unused {
4242 _assert(pkgInitConfig(*_config));
4243 _assert(pkgInitSystem(*_config, _system));
4248 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4249 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4251 [window_ orderFront: self];
4252 [window_ makeKey: self];
4253 [window_ _setHidden: NO];
4255 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
4256 [window_ setContentView:progress_];
4258 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4259 [progress_ setContentView:underlay_];
4261 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4264 [underlay_ addSubview:overlay_];
4266 database_ = [[Database alloc] init];
4267 [database_ setDelegate:progress_];
4269 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4270 0, 0, screenrect.size.width, screenrect.size.height - 48
4271 ) database:database_];
4273 [book_ setDelegate:self];
4275 [overlay_ addSubview:book_];
4277 NSArray *buttonitems = [NSArray arrayWithObjects:
4278 [NSDictionary dictionaryWithObjectsAndKeys:
4279 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4280 @"news-up.png", kUIButtonBarButtonInfo,
4281 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4282 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4283 self, kUIButtonBarButtonTarget,
4284 @"News", kUIButtonBarButtonTitle,
4285 @"0", kUIButtonBarButtonType,
4288 [NSDictionary dictionaryWithObjectsAndKeys:
4289 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4290 @"install-up.png", kUIButtonBarButtonInfo,
4291 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4292 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4293 self, kUIButtonBarButtonTarget,
4294 @"Install", kUIButtonBarButtonTitle,
4295 @"0", kUIButtonBarButtonType,
4298 [NSDictionary dictionaryWithObjectsAndKeys:
4299 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4300 @"changes-up.png", kUIButtonBarButtonInfo,
4301 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4302 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4303 self, kUIButtonBarButtonTarget,
4304 @"Changes", kUIButtonBarButtonTitle,
4305 @"0", kUIButtonBarButtonType,
4308 [NSDictionary dictionaryWithObjectsAndKeys:
4309 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4310 @"manage-up.png", kUIButtonBarButtonInfo,
4311 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4312 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4313 self, kUIButtonBarButtonTarget,
4314 @"Manage", kUIButtonBarButtonTitle,
4315 @"0", kUIButtonBarButtonType,
4318 [NSDictionary dictionaryWithObjectsAndKeys:
4319 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4320 @"search-up.png", kUIButtonBarButtonInfo,
4321 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4322 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4323 self, kUIButtonBarButtonTarget,
4324 @"Search", kUIButtonBarButtonTitle,
4325 @"0", kUIButtonBarButtonType,
4329 buttonbar_ = [[UIButtonBar alloc]
4331 withFrame:CGRectMake(
4332 0, screenrect.size.height - 48,
4333 screenrect.size.width, 48
4335 withItemList:buttonitems
4338 [buttonbar_ setDelegate:self];
4339 [buttonbar_ setBarStyle:1];
4340 [buttonbar_ setButtonBarTrackingMode:2];
4342 int buttons[5] = {1, 2, 3, 4, 5};
4343 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4344 [buttonbar_ showButtonGroup:0 withDuration:0];
4346 for (int i = 0; i != 5; ++i)
4347 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4348 i * 64 + 2, 1, 60, 48
4351 [buttonbar_ showSelectionForButton:1];
4352 [overlay_ addSubview:buttonbar_];
4354 [UIKeyboard initImplementationNow];
4355 CGSize keysize = [UIKeyboard defaultSize];
4356 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4357 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4358 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4363 [progress_ resetView];
4368 [self _setNewsPage];
4371 - (void) showKeyboard:(BOOL)show {
4373 [overlay_ addSubview:keyboard_];
4375 [keyboard_ removeFromSuperview];
4378 - (void) slideUp:(UIAlertSheet *)alert {
4379 [alert presentSheetFromButtonBar:buttonbar_];
4384 void AddPreferences(NSString *plist) {
4385 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4387 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4388 _assert(settings != NULL);
4389 NSMutableArray *items = [settings objectForKey:@"items"];
4393 for (size_t i(0); i != [items count]; ++i) {
4394 NSMutableDictionary *item([items objectAtIndex:i]);
4395 NSString *label = [item objectForKey:@"label"];
4396 if (label != nil && [label isEqualToString:@"Cydia"]) {
4403 for (size_t i(0); i != [items count]; ++i) {
4404 NSDictionary *item([items objectAtIndex:i]);
4405 NSString *label = [item objectForKey:@"label"];
4406 if (label != nil && [label isEqualToString:@"General"]) {
4407 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4408 @"CydiaSettings", @"bundle",
4409 @"PSLinkCell", @"cell",
4410 [NSNumber numberWithBool:YES], @"hasIcon",
4411 [NSNumber numberWithBool:YES], @"isController",
4413 nil] atIndex:(i + 1)];
4419 _assert([settings writeToFile:plist atomically:YES] == YES);
4426 id Alloc_(id self, SEL selector) {
4427 id object = alloc_(self, selector);
4428 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4433 id Dealloc_(id self, SEL selector) {
4434 id object = dealloc_(self, selector);
4435 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4439 int main(int argc, char *argv[]) {
4440 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4442 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4444 Home_ = NSHomeDirectory();
4447 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4448 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4449 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4450 Sounds_Keyboard_ = [keyboard boolValue];
4456 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4457 alloc_ = alloc->method_imp;
4458 alloc->method_imp = (IMP) &Alloc_;*/
4460 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4461 dealloc_ = dealloc->method_imp;
4462 dealloc->method_imp = (IMP) &Dealloc_;*/
4464 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4465 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4466 Firmware_ = strdup([prover UTF8String]);
4467 NSArray *versions = [prover componentsSeparatedByString:@"."];
4468 int count = [versions count];
4469 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4470 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4471 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4476 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4477 char *machine = new char[size];
4478 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4481 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4482 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4483 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4484 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4488 IOObjectRelease(service);
4491 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4492 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4494 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4495 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4497 Packages_ = [Metadata_ objectForKey:@"Packages"];
4499 setenv("CYDIA", "", _not(int));
4500 if (access("/User", F_OK) != 0)
4501 system("/usr/libexec/cydia/firmware.sh");
4502 system("dpkg --configure -a");
4504 space_ = CGColorSpaceCreateDeviceRGB();
4506 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4507 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4508 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4509 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4511 int value = UIApplicationMain(argc, argv, [Cydia class]);
4513 CGColorSpaceRelease(space_);