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>
54 #include <ext/stdio_filebuf.h>
56 #include <apt-pkg/acquire.h>
57 #include <apt-pkg/acquire-item.h>
58 #include <apt-pkg/algorithms.h>
59 #include <apt-pkg/cachefile.h>
60 #include <apt-pkg/configuration.h>
61 #include <apt-pkg/debmetaindex.h>
62 #include <apt-pkg/error.h>
63 #include <apt-pkg/init.h>
64 #include <apt-pkg/pkgrecords.h>
65 #include <apt-pkg/sourcelist.h>
66 #include <apt-pkg/sptr.h>
68 #include <sys/sysctl.h>
72 #include <mach-o/nlist.h>
82 /* Extension Keywords {{{ */
83 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
85 #define _assert(test) do \
87 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
92 #define _not(type) ((type) ~ (type) 0)
97 /* Miscellaneous Messages {{{ */
98 @interface NSString (Cydia)
99 - (NSString *) stringByAddingPercentEscapes;
100 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
103 /* External Constants {{{ */
104 extern NSString *kUIButtonBarButtonAction;
105 extern NSString *kUIButtonBarButtonInfo;
106 extern NSString *kUIButtonBarButtonInfoOffset;
107 extern NSString *kUIButtonBarButtonSelectedInfo;
108 extern NSString *kUIButtonBarButtonStyle;
109 extern NSString *kUIButtonBarButtonTag;
110 extern NSString *kUIButtonBarButtonTarget;
111 extern NSString *kUIButtonBarButtonTitle;
112 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
113 extern NSString *kUIButtonBarButtonTitleWidth;
114 extern NSString *kUIButtonBarButtonType;
117 /* iPhoneOS 2.0 Compatibility {{{ */
119 @interface UICGColor : NSObject {
122 - (id) initWithCGColor:(CGColorRef)color;
128 - (UIFont *) fontWithSize:(CGFloat)size;
131 @interface NSObject (iPhoneOS)
132 - (CGColorRef) cgColor;
133 - (CGColorRef) CGColor;
137 @implementation NSObject (iPhoneOS)
139 - (CGColorRef) cgColor {
140 return [self CGColor];
143 - (CGColorRef) CGColor {
144 return (CGColorRef) self;
148 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
153 @interface UITextView (iPhoneOS)
154 - (void) setTextSize:(float)size;
157 @implementation UITextView (iPhoneOS)
159 - (void) setTextSize:(float)size {
160 [self setFont:[[self font] fontWithSize:size]];
167 OBJC_EXPORT const char *class_getName(Class cls);
169 /* Reset View (UIView) {{{ */
170 @interface UIView (RVBook)
171 - (void) resetViewAnimated:(BOOL)animated;
175 @implementation UIView (RVBook)
177 - (void) resetViewAnimated:(BOOL)animated {
178 fprintf(stderr, "%s\n", class_getName(self->isa));
183 fprintf(stderr, "%s\n", class_getName(self->isa));
189 /* Reset View (UITable) {{{ */
190 @interface UITable (RVBook)
191 - (void) resetViewAnimated:(BOOL)animated;
195 @implementation UITable (RVBook)
197 - (void) resetViewAnimated:(BOOL)animated {
198 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
207 /* Reset View (UISectionList) {{{ */
208 @interface UISectionList (RVBook)
209 - (void) resetViewAnimated:(BOOL)animated;
213 @implementation UISectionList (RVBook)
215 - (void) resetViewAnimated:(BOOL)animated {
216 [[self table] resetViewAnimated:animated];
220 [[self table] clearView];
226 /* Perl-Compatible RegEx {{{ */
236 Pcre(const char *regex) :
241 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
244 fprintf(stderr, "%d:%s\n", offset, error);
248 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
249 matches_ = new int[(capture_ + 1) * 3];
257 NSString *operator [](size_t match) {
259 stringWithCString:(data_ + matches_[match * 2])
260 length:(matches_[match * 2 + 1] - matches_[match * 2])
264 bool operator ()(const char *data, size_t size) {
266 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
270 /* Mime Addresses {{{ */
271 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
273 @interface Address : NSObject {
279 - (NSString *) email;
281 + (Address *) addressWithString:(NSString *)string;
282 - (Address *) initWithString:(NSString *)string;
285 @implementation Address
294 - (NSString *) name {
298 - (NSString *) email {
302 + (Address *) addressWithString:(NSString *)string {
303 return [[[Address alloc] initWithString:string] autorelease];
306 - (Address *) initWithString:(NSString *)string {
307 if ((self = [super init]) != nil) {
308 const char *data = [string UTF8String];
309 size_t size = [string length];
311 if (email_r(data, size)) {
312 name_ = [email_r[1] retain];
313 email_ = [email_r[2] retain];
315 name_ = [[NSString stringWithCString:data length:size] retain];
323 /* CoreGraphics Primitives {{{ */
334 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
337 Set(space, red, green, blue, alpha);
342 CGColorRelease(color_);
349 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
351 float color[] = {red, green, blue, alpha};
352 color_ = CGColorCreate(space, color);
355 operator CGColorRef() {
370 /* Right Alignment {{{ */
371 @interface UIRightTextLabel : UITextLabel {
372 float _savedRightEdgeX;
373 BOOL _sizedtofit_flag;
376 - (void) setFrame:(CGRect)frame;
377 - (void) setText:(NSString *)text;
378 - (void) realignText;
381 @implementation UIRightTextLabel
383 - (void) setFrame:(CGRect)frame {
384 [super setFrame:frame];
385 if (_sizedtofit_flag == NO) {
386 _savedRightEdgeX = frame.origin.x;
391 - (void) setText:(NSString *)text {
392 [super setText:text];
396 - (void) realignText {
397 CGRect oldFrame = [self frame];
399 _sizedtofit_flag = YES;
400 [self sizeToFit]; // shrink down size so I can right align it
402 CGRect newFrame = [self frame];
404 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
405 oldFrame.size.width = newFrame.size.width;
406 [super setFrame:oldFrame];
407 _sizedtofit_flag = NO;
413 /* Random Global Variables {{{ */
414 static const int PulseInterval_ = 50000;
416 static CGColor Black_;
417 static CGColor Clear_;
419 static CGColor White_;
421 static NSString *Home_;
422 static BOOL Sounds_Keyboard_;
424 const char *Firmware_ = NULL;
425 const char *Machine_ = NULL;
426 const char *SerialNumber_ = NULL;
432 CGColorSpaceRef space_;
434 #define FW_LEAST(major, minor, bugfix) \
435 (major < Major_ || major == Major_ && \
436 (minor < Minor_ || minor == Minor_ && \
442 static NSMutableDictionary *Metadata_;
443 static NSMutableDictionary *Packages_;
446 NSString *GetLastUpdate() {
447 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
450 return @"Never or Unknown";
452 CFLocaleRef locale = CFLocaleCopyCurrent();
453 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
454 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
456 CFRelease(formatter);
459 return [(NSString *) formatted autorelease];
462 /* Display Helpers {{{ */
463 inline float Interpolate(float begin, float end, float fraction) {
464 return (end - begin) * fraction + begin;
467 NSString *SizeString(double size) {
469 while (size > 1024) {
474 static const char *powers_[] = {"B", "kB", "MB", "GB"};
476 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
479 static const float TextViewOffset_ = 22;
481 UITextView *GetTextView(NSString *value, float left, bool html) {
482 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
483 [text setEditable:NO];
484 [text setTextSize:16];
486 [text setHTML:value];
488 [text setText:value];
489 [text setEnabled:NO];
491 [text setBackgroundColor:Clear_];
493 CGRect frame = [text frame];
494 [text setFrame:frame];
495 CGRect rect = [text visibleTextRect];
496 frame.size.height = rect.size.height;
497 [text setFrame:frame];
502 NSString *Simplify(NSString *title) {
503 const char *data = [title UTF8String];
504 size_t size = [title length];
506 Pcre title_r("^(.*?)( \\(.*\\))?$");
507 if (title_r(data, size))
514 /* Delegate Prototypes {{{ */
518 @protocol ProgressDelegate
519 - (void) setProgressError:(NSString *)error;
520 - (void) setProgressTitle:(NSString *)title;
521 - (void) setProgressPercent:(float)percent;
522 - (void) addProgressOutput:(NSString *)output;
525 @protocol CydiaDelegate
526 - (void) installPackage:(Package *)package;
527 - (void) removePackage:(Package *)package;
528 - (void) slideUp:(UIAlertSheet *)alert;
529 - (void) distUpgrade;
533 /* Status Delegation {{{ */
535 public pkgAcquireStatus
538 _transient id<ProgressDelegate> delegate_;
546 void setDelegate(id delegate) {
547 delegate_ = delegate;
550 virtual bool MediaChange(std::string media, std::string drive) {
554 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
557 virtual void Fetch(pkgAcquire::ItemDesc &item) {
558 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
561 virtual void Done(pkgAcquire::ItemDesc &item) {
564 virtual void Fail(pkgAcquire::ItemDesc &item) {
566 item.Owner->Status == pkgAcquire::Item::StatIdle ||
567 item.Owner->Status == pkgAcquire::Item::StatDone
571 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
574 virtual bool Pulse(pkgAcquire *Owner) {
575 bool value = pkgAcquireStatus::Pulse(Owner);
578 double(CurrentBytes + CurrentItems) /
579 double(TotalBytes + TotalItems)
582 [delegate_ setProgressPercent:percent];
586 virtual void Start() {
589 virtual void Stop() {
593 /* Progress Delegation {{{ */
598 _transient id<ProgressDelegate> delegate_;
601 virtual void Update() {
602 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
603 [delegate_ setProgressPercent:(Percent / 100)];
612 void setDelegate(id delegate) {
613 delegate_ = delegate;
616 virtual void Done() {
617 [delegate_ setProgressPercent:1];
622 /* Database Interface {{{ */
623 @interface Database : NSObject {
625 pkgRecords *records_;
626 pkgProblemResolver *resolver_;
627 pkgAcquire *fetcher_;
629 SPtr<pkgPackageManager> manager_;
630 pkgSourceList *list_;
632 NSMutableDictionary *sources_;
633 NSMutableArray *packages_;
635 _transient id delegate_;
641 - (void) _readStatus:(NSNumber *)fd;
642 - (void) _readOutput:(NSNumber *)fd;
644 - (Package *) packageWithName:(NSString *)name;
647 - (pkgCacheFile &) cache;
648 - (pkgRecords *) records;
649 - (pkgProblemResolver *) resolver;
650 - (pkgAcquire &) fetcher;
651 - (NSArray *) packages;
659 - (void) updateWithStatus:(Status &)status;
661 - (void) setDelegate:(id)delegate;
662 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
666 /* Source Class {{{ */
667 @interface Source : NSObject {
668 NSString *description_;
673 NSString *distribution_;
677 NSString *defaultIcon_;
682 - (Source *) initWithMetaIndex:(metaIndex *)index;
687 - (NSString *) distribution;
690 - (NSString *) description;
691 - (NSString *) label;
692 - (NSString *) origin;
693 - (NSString *) version;
695 - (NSString *) defaultIcon;
698 @implementation Source
702 [distribution_ release];
705 if (description_ != nil)
706 [description_ release];
713 if (defaultIcon_ != nil)
714 [defaultIcon_ release];
719 - (Source *) initWithMetaIndex:(metaIndex *)index {
720 if ((self = [super init]) != nil) {
721 trusted_ = index->IsTrusted();
723 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
724 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
725 type_ = [[NSString stringWithCString:index->GetType()] retain];
733 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
734 if (dindex != NULL) {
735 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
737 while (std::getline(release, line)) {
738 std::string::size_type colon(line.find(':'));
739 if (colon == std::string::npos)
742 std::string name(line.substr(0, colon));
743 std::string value(line.substr(colon + 1));
744 while (!value.empty() && value[0] == ' ')
745 value = value.substr(1);
747 if (name == "Default-Icon")
748 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
749 else if (name == "Description")
750 description_ = [[NSString stringWithCString:value.c_str()] retain];
751 else if (name == "Label")
752 label_ = [[NSString stringWithCString:value.c_str()] retain];
753 else if (name == "Origin")
754 origin_ = [[NSString stringWithCString:value.c_str()] retain];
755 else if (name == "Version")
756 version_ = [[NSString stringWithCString:value.c_str()] retain];
770 - (NSString *) distribution {
771 return distribution_;
774 - (NSString *) type {
778 - (NSString *) description {
782 - (NSString *) label {
786 - (NSString *) origin {
790 - (NSString *) version {
794 - (NSString *) defaultIcon {
800 /* Package Class {{{ */
801 NSString *Scour(const char *field, const char *begin, const char *end) {
802 size_t i(0), l(strlen(field));
805 const char *name = begin + i;
806 const char *colon = name + l;
807 const char *value = colon + 1;
812 memcmp(name, field, l) == 0
814 while (value != end && value[0] == ' ')
816 const char *line = std::find(value, end, '\n');
817 while (line != value && line[-1] == ' ')
819 return [NSString stringWithCString:value length:(line - value)];
821 begin = std::find(begin, end, '\n');
829 @interface Package : NSObject {
830 pkgCache::PkgIterator iterator_;
831 _transient Database *database_;
832 pkgCache::VerIterator version_;
833 pkgCache::VerFileIterator file_;
837 NSString *installed_;
846 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
847 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
849 - (NSString *) section;
850 - (Address *) maintainer;
852 - (NSString *) description;
853 - (NSString *) index;
857 - (NSString *) latest;
858 - (NSString *) installed;
865 - (NSString *) tagline;
867 - (NSString *) website;
871 - (BOOL) matches:(NSString *)text;
873 - (NSComparisonResult) compareByName:(Package *)package;
874 - (NSComparisonResult) compareBySection:(Package *)package;
875 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
876 - (NSComparisonResult) compareForChanges:(Package *)package;
881 - (NSNumber *) isSearchedForBy:(NSString *)search;
882 - (NSNumber *) isInstalledInSection:(NSString *)section;
883 - (NSNumber *) isUninstalledInSection:(NSString *)section;
887 @implementation Package
891 if (installed_ != nil)
892 [installed_ release];
908 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
909 if ((self = [super init]) != nil) {
910 iterator_ = iterator;
911 database_ = database;
916 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
918 const char *begin, *end;
919 parser->GetRec(begin, end);
921 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
922 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
924 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
925 name_ = Scour("Name", begin, end);
927 name_ = [name_ retain];
928 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
929 icon_ = Scour("Icon", begin, end);
931 icon_ = [icon_ retain];
932 website_ = Scour("Website", begin, end);
934 website_ = [website_ retain];
936 source_ = [[database_ getSource:file_.File()] retain];
938 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
939 if (metadata == nil || [metadata count] == 0) {
940 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
944 [Packages_ setObject:metadata forKey:id_];
949 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
950 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
951 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
952 return [[[Package alloc]
953 initWithIterator:iterator
961 - (NSString *) section {
962 const char *section = iterator_.Section();
963 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
966 - (Address *) maintainer {
967 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
968 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
972 return version_->InstalledSize;
975 - (NSString *) description {
976 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
977 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
979 NSArray *lines = [description componentsSeparatedByString:@"\n"];
980 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
981 if ([lines count] < 2)
984 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
985 for (size_t i(1); i != [lines count]; ++i) {
986 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
987 [trimmed addObject:trim];
990 return [trimmed componentsJoinedByString:@"\n"];
993 - (NSString *) index {
994 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
995 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
999 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1002 - (NSString *) latest {
1006 - (NSString *) installed {
1010 - (BOOL) upgradable {
1011 if (NSString *installed = [self installed])
1012 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1014 return [self essential];
1017 - (BOOL) essential {
1018 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1022 return (*[database_ cache])[iterator_].InstBroken();
1029 - (NSString *) name {
1030 return name_ == nil ? id_ : name_;
1033 - (NSString *) tagline {
1037 - (NSString *) icon {
1041 - (NSString *) website {
1045 - (Source *) source {
1049 - (BOOL) matches:(NSString *)text {
1055 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1056 if (range.location != NSNotFound)
1059 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1060 if (range.location != NSNotFound)
1063 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1064 if (range.location != NSNotFound)
1070 - (NSComparisonResult) compareByName:(Package *)package {
1071 NSString *lhs = [self name];
1072 NSString *rhs = [package name];
1074 if ([lhs length] != 0 && [rhs length] != 0) {
1075 unichar lhc = [lhs characterAtIndex:0];
1076 unichar rhc = [rhs characterAtIndex:0];
1078 if (isalpha(lhc) && !isalpha(rhc))
1079 return NSOrderedAscending;
1080 else if (!isalpha(lhc) && isalpha(rhc))
1081 return NSOrderedDescending;
1084 return [lhs caseInsensitiveCompare:rhs];
1087 - (NSComparisonResult) compareBySection:(Package *)package {
1088 NSString *lhs = [self section];
1089 NSString *rhs = [package section];
1091 if (lhs == NULL && rhs != NULL)
1092 return NSOrderedAscending;
1093 else if (lhs != NULL && rhs == NULL)
1094 return NSOrderedDescending;
1095 else if (lhs != NULL && rhs != NULL) {
1096 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1097 if (result != NSOrderedSame)
1101 return NSOrderedSame;
1104 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1105 NSString *lhs = [self section];
1106 NSString *rhs = [package section];
1108 if (lhs == NULL && rhs != NULL)
1109 return NSOrderedAscending;
1110 else if (lhs != NULL && rhs == NULL)
1111 return NSOrderedDescending;
1112 else if (lhs != NULL && rhs != NULL) {
1113 NSComparisonResult result = [lhs compare:rhs];
1114 if (result != NSOrderedSame)
1118 return [self compareByName:package];
1121 - (NSComparisonResult) compareForChanges:(Package *)package {
1122 BOOL lhs = [self upgradable];
1123 BOOL rhs = [package upgradable];
1126 return lhs ? NSOrderedAscending : NSOrderedDescending;
1128 switch ([[self seen] compare:[package seen]]) {
1129 case NSOrderedAscending:
1130 return NSOrderedDescending;
1133 case NSOrderedDescending:
1134 return NSOrderedAscending;
1140 return [self compareByName:package];
1144 pkgProblemResolver *resolver = [database_ resolver];
1145 resolver->Clear(iterator_);
1146 resolver->Protect(iterator_);
1147 pkgCacheFile &cache([database_ cache]);
1148 cache->MarkInstall(iterator_, false);
1149 pkgDepCache::StateCache &state((*cache)[iterator_]);
1150 if (!state.Install())
1151 cache->SetReInstall(iterator_, true);
1155 pkgProblemResolver *resolver = [database_ resolver];
1156 resolver->Clear(iterator_);
1157 resolver->Protect(iterator_);
1158 resolver->Remove(iterator_);
1159 [database_ cache]->MarkDelete(iterator_, true);
1162 - (NSNumber *) isSearchedForBy:(NSString *)search {
1163 return [NSNumber numberWithBool:[self matches:search]];
1166 - (NSNumber *) isInstalledInSection:(NSString *)section {
1167 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1170 - (NSNumber *) isUninstalledInSection:(NSString *)section {
1171 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1176 /* Section Class {{{ */
1177 @interface Section : NSObject {
1183 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1184 - (NSString *) name;
1187 - (void) addToCount;
1191 @implementation Section
1198 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1199 if ((self = [super init]) != nil) {
1200 name_ = [name retain];
1205 - (NSString *) name {
1217 - (void) addToCount {
1224 /* Database Implementation {{{ */
1225 @implementation Database
1232 - (void) _readStatus:(NSNumber *)fd {
1233 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1235 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1236 std::istream is(&ib);
1241 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1243 pcre_extra *study = NULL;
1245 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1246 int matches[(capture + 1) * 3];
1248 while (std::getline(is, line)) {
1249 const char *data(line.c_str());
1251 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1253 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1256 [delegate_ setProgressPercent:(percent / 100)];
1258 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1259 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1261 if (type == "pmerror")
1262 [delegate_ setProgressError:string];
1263 else if (type == "pmstatus")
1264 [delegate_ setProgressTitle:string];
1265 else if (type == "pmconffile")
1267 else _assert(false);
1274 - (void) _readOutput:(NSNumber *)fd {
1275 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1277 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1278 std::istream is(&ib);
1281 while (std::getline(is, line))
1282 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1288 - (Package *) packageWithName:(NSString *)name {
1289 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1290 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1293 - (Database *) init {
1294 if ((self = [super init]) != nil) {
1300 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1301 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1305 _assert(pipe(fds) != -1);
1309 detachNewThreadSelector:@selector(_readStatus:)
1311 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1314 _assert(pipe(fds) != -1);
1315 _assert(dup2(fds[1], 1) != -1);
1316 _assert(close(fds[1]) != -1);
1319 detachNewThreadSelector:@selector(_readOutput:)
1321 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1326 - (pkgCacheFile &) cache {
1330 - (pkgRecords *) records {
1334 - (pkgProblemResolver *) resolver {
1338 - (pkgAcquire &) fetcher {
1342 - (NSArray *) packages {
1346 - (void) reloadData {
1356 if (!cache_.Open(progress_, true)) {
1357 fprintf(stderr, "repairing corrupted database...\n");
1359 [self updateWithStatus:status_];
1360 _assert(cache_.Open(progress_, true));
1363 now_ = [[NSDate date] retain];
1365 records_ = new pkgRecords(cache_);
1366 resolver_ = new pkgProblemResolver(cache_);
1367 fetcher_ = new pkgAcquire(&status_);
1370 list_ = new pkgSourceList();
1371 _assert(list_->ReadMainList());
1373 [sources_ removeAllObjects];
1374 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1375 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1376 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1378 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1379 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1383 [packages_ removeAllObjects];
1384 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1385 if (Package *package = [Package packageWithIterator:iterator database:self])
1386 if ([package source] != nil || [package installed] != nil)
1387 [packages_ addObject:package];
1391 pkgRecords records(cache_);
1393 lock_ = new FileFd();
1394 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1395 _assert(!_error->PendingError());
1398 // XXX: explain this with an error message
1399 _assert(list.ReadMainList());
1401 manager_ = (_system->CreatePM(cache_));
1402 _assert(manager_->GetArchives(fetcher_, &list, &records));
1403 _assert(!_error->PendingError());
1407 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1409 _assert(list.ReadMainList());
1410 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1411 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1414 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1418 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1420 if (result == pkgPackageManager::Failed)
1422 if (_error->PendingError())
1424 if (result != pkgPackageManager::Completed)
1427 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1429 _assert(list.ReadMainList());
1430 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1431 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1434 if (![before isEqualToArray:after])
1439 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1440 _assert(pkgApplyStatus(cache_));
1442 if (cache_->BrokenCount() != 0) {
1443 _assert(pkgFixBroken(cache_));
1444 _assert(cache_->BrokenCount() == 0);
1445 _assert(pkgMinimizeUpgrade(cache_));
1448 _assert(pkgDistUpgrade(cache_));
1452 [self updateWithStatus:status_];
1455 - (void) updateWithStatus:(Status &)status {
1457 _assert(list.ReadMainList());
1460 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1461 _assert(!_error->PendingError());
1463 pkgAcquire fetcher(&status);
1464 _assert(list.GetIndexes(&fetcher));
1466 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1467 bool failed = false;
1468 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1469 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1470 (*item)->Finished();
1474 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1475 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1476 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1479 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1483 - (void) setDelegate:(id)delegate {
1484 delegate_ = delegate;
1485 status_.setDelegate(delegate);
1486 progress_.setDelegate(delegate);
1489 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1490 pkgIndexFile *index(NULL);
1491 list_->FindIndex(file, index);
1492 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1498 /* RVPage Interface {{{ */
1501 @interface RVPage : UIView {
1502 _transient RVBook *book_;
1503 _transient id delegate_;
1506 - (NSString *) title;
1507 - (NSString *) backButtonTitle;
1508 - (NSString *) rightButtonTitle;
1509 - (NSString *) leftButtonTitle;
1510 - (UIView *) accessoryView;
1512 - (void) _rightButtonClicked;
1513 - (void) _leftButtonClicked;
1515 - (void) setPageActive:(BOOL)active;
1516 - (void) resetViewAnimated:(BOOL)animated;
1518 - (void) setTitle:(NSString *)title;
1519 - (void) setBackButtonTitle:(NSString *)title;
1521 - (void) reloadButtons;
1522 - (void) reloadData;
1524 - (id) initWithBook:(RVBook *)book;
1526 - (void) setDelegate:(id)delegate;
1530 /* Reset View {{{ */
1531 @protocol RVDelegate
1532 - (void) setPageActive:(BOOL)active with:(id)object;
1533 - (void) resetViewAnimated:(BOOL)animated with:(id)object;
1534 - (void) reloadDataWith:(id)object;
1537 @interface RVBook : UIView {
1538 NSMutableArray *pages_;
1539 UINavigationBar *navbar_;
1540 UITransitionView *transition_;
1542 _transient id delegate_;
1545 - (id) initWithFrame:(CGRect)frame;
1546 - (void) setDelegate:(id)delegate;
1548 - (void) setPage:(RVPage *)page;
1550 - (void) pushPage:(RVPage *)page;
1551 - (void) popPages:(unsigned)pages;
1553 - (void) setPrompt:(NSString *)prompt;
1555 - (void) resetViewAnimated:(BOOL)animated;
1556 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
1558 - (void) setTitle:(NSString *)title forPage:(RVPage *)page;
1559 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
1560 - (void) reloadButtonsForPage:(RVPage *)page;
1562 - (void) reloadData;
1564 - (CGRect) pageBounds;
1568 @implementation RVBook
1571 [navbar_ setDelegate:nil];
1575 [transition_ release];
1579 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1580 _assert([pages_ count] != 0);
1581 RVPage *page = [pages_ lastObject];
1583 case 0: [page _rightButtonClicked]; break;
1584 case 1: [page _leftButtonClicked]; break;
1588 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
1589 _assert([pages_ count] != 0);
1591 [[pages_ lastObject] setPageActive:NO];
1592 [pages_ removeLastObject];
1594 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1597 - (id) initWithFrame:(CGRect)frame {
1598 if ((self = [super initWithFrame:frame]) != nil) {
1599 pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
1601 struct CGRect bounds = [self bounds];
1602 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
1603 CGRect navrect = {{0, 0}, navsize};
1605 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1606 [self addSubview:navbar_];
1608 [navbar_ setBarStyle:1];
1609 [navbar_ setDelegate:self];
1611 [navbar_ setPrompt:@""];
1613 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
1614 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
1617 [self addSubview:transition_];
1621 - (void) setDelegate:(id)delegate {
1622 delegate_ = delegate;
1625 - (void) setPage:(RVPage *)page {
1626 if ([pages_ count] != 0)
1627 [[pages_ lastObject] setPageActive:NO];
1629 [navbar_ disableAnimation];
1631 for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
1632 [navbar_ popNavigationItem];
1635 [self pushPage:page];
1636 [navbar_ enableAnimation];
1639 - (void) pushPage:(RVPage *)page {
1640 if ([pages_ count] != 0)
1641 [[pages_ lastObject] setPageActive:NO];
1643 NSString *title = Simplify([page title]);
1645 NSString *backButtonTitle = [page backButtonTitle];
1646 if (backButtonTitle == nil)
1647 backButtonTitle = title;
1649 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1650 [navitem setBackButtonTitle:backButtonTitle];
1651 [navbar_ pushNavigationItem:navitem];
1653 BOOL animated = [pages_ count] == 0 ? NO : YES;
1654 [transition_ transition:(animated ? 1 : 0) toView:page];
1655 [page setPageActive:YES];
1657 [pages_ addObject:page];
1658 [self reloadButtonsForPage:page];
1660 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
1663 - (void) popPages:(unsigned)pages {
1667 [[pages_ lastObject] setPageActive:NO];
1670 for (unsigned i(0); i != pages; ++i)
1671 [navbar_ popNavigationItem];
1674 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1677 - (void) setPrompt:(NSString *)prompt {
1678 [navbar_ setPrompt:prompt];
1681 - (void) resetViewAnimated:(BOOL)animated {
1684 if ([pages_ count] > 1) {
1685 [navbar_ disableAnimation];
1686 while ([pages_ count] != (animated ? 2 : 1))
1687 [navbar_ popNavigationItem];
1688 [navbar_ enableAnimation];
1690 [navbar_ popNavigationItem];
1695 [self resetViewAnimated:animated toPage:[pages_ lastObject]];
1698 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
1699 [page resetViewAnimated:animated];
1700 [transition_ transition:(animated ? 2 : 0) toView:page];
1701 [page setPageActive:YES];
1702 [self reloadButtonsForPage:page];
1703 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
1706 - (void) setTitle:(NSString *)title forPage:(RVPage *)page {
1707 if ([pages_ count] == 0 || page != [pages_ lastObject])
1709 UINavigationItem *navitem = [navbar_ topItem];
1710 [navitem setTitle:title];
1713 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
1714 if ([pages_ count] == 0 || page != [pages_ lastObject])
1716 UINavigationItem *navitem = [navbar_ topItem];
1717 [navitem setBackButtonTitle:title];
1720 - (void) reloadButtonsForPage:(RVPage *)page {
1721 if ([pages_ count] == 0 || page != [pages_ lastObject])
1723 NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
1724 [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
1727 - (void) reloadData {
1728 for (int i(0), e([pages_ count]); i != e; ++i) {
1729 RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
1734 - (CGRect) pageBounds {
1735 return [transition_ bounds];
1740 /* RVPage Implementation {{{ */
1741 @implementation RVPage
1743 - (NSString *) title {
1744 [self doesNotRecognizeSelector:_cmd];
1748 - (NSString *) backButtonTitle {
1752 - (NSString *) leftButtonTitle {
1756 - (NSString *) rightButtonTitle {
1760 - (void) _rightButtonClicked {
1761 [self doesNotRecognizeSelector:_cmd];
1764 - (void) _leftButtonClicked {
1765 [self doesNotRecognizeSelector:_cmd];
1768 - (UIView *) accessoryView {
1772 - (void) setPageActive:(BOOL)active {
1775 - (void) resetViewAnimated:(BOOL)animated {
1776 [self doesNotRecognizeSelector:_cmd];
1779 - (void) setTitle:(NSString *)title {
1780 [book_ setTitle:title forPage:self];
1783 - (void) setBackButtonTitle:(NSString *)title {
1784 [book_ setBackButtonTitle:title forPage:self];
1787 - (void) reloadButtons {
1788 [book_ reloadButtonsForPage:self];
1791 - (void) reloadData {
1794 - (id) initWithBook:(RVBook *)book {
1795 if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
1800 - (void) setDelegate:(id)delegate {
1801 delegate_ = delegate;
1807 /* Confirmation View {{{ */
1808 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1809 if ([packages count] == 0)
1812 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1813 [fields setObject:text forKey:key];
1815 CGColor blue(space_, 0, 0, 0.4, 1);
1816 [text setTextColor:blue];
1819 @protocol ConfirmationViewDelegate
1824 @interface ConfirmationView : UIView {
1825 Database *database_;
1827 UITransitionView *transition_;
1829 UINavigationBar *navbar_;
1830 UIPreferencesTable *table_;
1831 NSMutableDictionary *fields_;
1832 UIAlertSheet *essential_;
1837 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1841 @implementation ConfirmationView
1844 [navbar_ setDelegate:nil];
1845 [transition_ setDelegate:nil];
1846 [table_ setDataSource:nil];
1848 [transition_ release];
1853 if (essential_ != nil)
1854 [essential_ release];
1859 [transition_ transition:7 toView:nil];
1863 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1864 if (from != nil && to == nil)
1865 [self removeFromSuperview];
1868 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1871 if (essential_ != nil)
1872 [essential_ popupAlertAnimated:YES];
1874 [delegate_ confirm];
1883 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1884 [essential_ dismiss];
1888 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1892 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1894 case 0: return @"Statistics";
1895 case 1: return @"Modifications";
1897 default: _assert(false);
1901 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1904 case 1: return [fields_ count];
1906 default: _assert(false);
1910 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1911 if (group != 1 || row == -1)
1914 _assert(size_t(row) < [fields_ count]);
1915 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1919 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1920 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1921 [cell setShowSelection:NO];
1924 case 0: switch (row) {
1926 [cell setTitle:@"Downloading"];
1927 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1931 [cell setTitle:@"Resuming At"];
1932 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1936 double size([database_ cache]->UsrSize());
1939 [cell setTitle:@"Disk Freeing"];
1940 [cell setValue:SizeString(-size)];
1942 [cell setTitle:@"Disk Using"];
1943 [cell setValue:SizeString(size)];
1947 default: _assert(false);
1951 _assert(size_t(row) < [fields_ count]);
1952 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1953 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1956 default: _assert(false);
1962 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1963 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1964 database_ = database;
1965 delegate_ = delegate;
1967 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1968 [self addSubview:transition_];
1970 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1972 CGSize navsize = [UINavigationBar defaultSize];
1973 CGRect navrect = {{0, 0}, navsize};
1974 CGRect bounds = [overlay_ bounds];
1976 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1977 [navbar_ setBarStyle:1];
1978 [navbar_ setDelegate:self];
1980 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1981 [navbar_ pushNavigationItem:navitem];
1982 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1984 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1986 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1987 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1988 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1989 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1990 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
1994 pkgCacheFile &cache([database_ cache]);
1995 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
1996 Package *package([Package packageWithIterator:iterator database:database_]);
1997 NSString *name([package name]);
1998 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
1999 pkgDepCache::StateCache &state(cache[iterator]);
2001 if (state.NewInstall())
2002 [installing addObject:name];
2003 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2004 [reinstalling addObject:name];
2005 else if (state.Upgrade())
2006 [upgrading addObject:name];
2007 else if (state.Downgrade())
2008 [downgrading addObject:name];
2009 else if (state.Delete()) {
2012 [removing addObject:name];
2019 essential_ = [[UIAlertSheet alloc]
2020 initWithTitle:@"Unable to Comply"
2021 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2022 defaultButtonIndex:0
2027 [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."];
2030 AddTextView(fields_, installing, @"Installing");
2031 AddTextView(fields_, reinstalling, @"Reinstalling");
2032 AddTextView(fields_, upgrading, @"Upgrading");
2033 AddTextView(fields_, downgrading, @"Downgrading");
2034 AddTextView(fields_, removing, @"Removing");
2036 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2037 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2040 [table_ setReusesTableCells:YES];
2041 [table_ setDataSource:self];
2042 [table_ reloadData];
2044 [overlay_ addSubview:navbar_];
2045 [overlay_ addSubview:table_];
2047 [view addSubview:self];
2049 [transition_ setDelegate:self];
2051 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2052 [transition_ transition:0 toView:blank];
2053 [transition_ transition:3 toView:overlay_];
2060 /* Progress Data {{{ */
2061 @interface ProgressData : NSObject {
2067 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2074 @implementation ProgressData
2076 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2077 if ((self = [super init]) != nil) {
2078 selector_ = selector;
2098 /* Progress View {{{ */
2099 @interface ProgressView : UIView <
2103 UIView *background_;
2104 UITransitionView *transition_;
2106 UINavigationBar *navbar_;
2107 UIProgressBar *progress_;
2108 UITextView *output_;
2109 UITextLabel *status_;
2113 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2115 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
2116 - (void) setContentView:(UIView *)view;
2119 - (void) _retachThread;
2120 - (void) _detachNewThreadData:(ProgressData *)data;
2121 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2125 @protocol ProgressViewDelegate
2126 - (void) progressViewIsComplete:(ProgressView *)sender;
2129 @implementation ProgressView
2132 [transition_ setDelegate:nil];
2133 [navbar_ setDelegate:nil];
2136 if (background_ != nil)
2137 [background_ release];
2138 [transition_ release];
2141 [progress_ release];
2147 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2148 if (bootstrap_ && from == overlay_ && to == view_)
2152 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
2153 if ((self = [super initWithFrame:frame]) != nil) {
2154 delegate_ = delegate;
2156 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2157 [transition_ setDelegate:self];
2159 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2162 [overlay_ setBackgroundColor:Black_];
2164 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2165 [background_ setBackgroundColor:Black_];
2166 [self addSubview:background_];
2169 [self addSubview:transition_];
2171 CGSize navsize = [UINavigationBar defaultSize];
2172 CGRect navrect = {{0, 0}, navsize};
2174 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2175 [overlay_ addSubview:navbar_];
2177 [navbar_ setBarStyle:1];
2178 [navbar_ setDelegate:self];
2180 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2181 [navbar_ pushNavigationItem:navitem];
2183 CGRect bounds = [overlay_ bounds];
2184 CGSize prgsize = [UIProgressBar defaultSize];
2187 (bounds.size.width - prgsize.width) / 2,
2188 bounds.size.height - prgsize.height - 20
2191 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2192 [overlay_ addSubview:progress_];
2194 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2196 bounds.size.height - prgsize.height - 50,
2197 bounds.size.width - 20,
2201 [status_ setColor:White_];
2202 [status_ setBackgroundColor:Clear_];
2204 [status_ setCentersHorizontally:YES];
2205 //[status_ setFont:font];
2207 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2209 navrect.size.height + 20,
2210 bounds.size.width - 20,
2211 bounds.size.height - navsize.height - 62 - navrect.size.height
2214 //[output_ setTextFont:@"Courier New"];
2215 [output_ setTextSize:12];
2217 [output_ setTextColor:White_];
2218 [output_ setBackgroundColor:Clear_];
2220 [output_ setMarginTop:0];
2221 [output_ setAllowsRubberBanding:YES];
2223 [overlay_ addSubview:output_];
2224 [overlay_ addSubview:status_];
2226 [progress_ setStyle:0];
2230 - (void) setContentView:(UIView *)view {
2231 view_ = [view retain];
2234 - (void) resetView {
2235 [transition_ transition:6 toView:view_];
2238 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2242 - (void) _retachThread {
2243 [delegate_ progressViewIsComplete:self];
2247 - (void) _detachNewThreadData:(ProgressData *)data {
2248 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2250 [[data target] performSelector:[data selector] withObject:[data object]];
2253 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2258 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2259 [navbar_ popNavigationItem];
2260 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
2261 [navbar_ pushNavigationItem:navitem];
2263 [status_ setText:nil];
2264 [output_ setText:@""];
2265 [progress_ setProgress:0];
2267 [transition_ transition:6 toView:overlay_];
2270 detachNewThreadSelector:@selector(_detachNewThreadData:)
2272 withObject:[[ProgressData alloc]
2273 initWithSelector:selector
2280 - (void) setProgressError:(NSString *)error {
2282 performSelectorOnMainThread:@selector(_setProgressError:)
2288 - (void) setProgressTitle:(NSString *)title {
2290 performSelectorOnMainThread:@selector(_setProgressTitle:)
2296 - (void) setProgressPercent:(float)percent {
2298 performSelectorOnMainThread:@selector(_setProgressPercent:)
2299 withObject:[NSNumber numberWithFloat:percent]
2304 - (void) addProgressOutput:(NSString *)output {
2306 performSelectorOnMainThread:@selector(_addProgressOutput:)
2312 - (void) _setProgressError:(NSString *)error {
2313 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2314 initWithTitle:@"Package Error"
2315 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2316 defaultButtonIndex:0
2321 [sheet setBodyText:error];
2322 [sheet popupAlertAnimated:YES];
2325 - (void) _setProgressTitle:(NSString *)title {
2326 [status_ setText:[title stringByAppendingString:@"..."]];
2329 - (void) _setProgressPercent:(NSNumber *)percent {
2330 [progress_ setProgress:[percent floatValue]];
2333 - (void) _addProgressOutput:(NSString *)output {
2334 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2335 CGSize size = [output_ contentSize];
2336 CGRect rect = {{0, size.height}, {size.width, 0}};
2337 [output_ scrollRectToVisible:rect animated:YES];
2343 /* Package Cell {{{ */
2344 @interface PackageCell : UITableCell {
2347 UITextLabel *description_;
2348 UITextLabel *source_;
2349 UIImageView *trusted_;
2352 - (PackageCell *) init;
2353 - (void) setPackage:(Package *)package;
2355 - (void) _setSelected:(float)fraction;
2356 - (void) setSelected:(BOOL)selected;
2357 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2358 - (void) _setSelectionFadeFraction:(float)fraction;
2362 @implementation PackageCell
2367 [description_ release];
2373 - (PackageCell *) init {
2374 if ((self = [super init]) != nil) {
2375 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2376 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2377 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2379 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2381 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2382 [name_ setBackgroundColor:Clear_];
2383 [name_ setFont:bold];
2385 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2386 [source_ setBackgroundColor:Clear_];
2387 [source_ setFont:large];
2389 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2390 [description_ setBackgroundColor:Clear_];
2391 [description_ setFont:small];
2393 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2394 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
2396 [self addSubview:icon_];
2397 [self addSubview:name_];
2398 [self addSubview:description_];
2399 [self addSubview:source_];
2407 - (void) setPackage:(Package *)package {
2408 Source *source = [package source];
2410 UIImage *image = nil;
2411 if (NSString *icon = [package icon])
2412 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2413 if (image == nil) if (NSString *icon = [source defaultIcon])
2414 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2416 image = [UIImage applicationImageNamed:@"unknown.png"];
2417 [icon_ setImage:image];
2420 CGSize size = [image size];
2421 float scale = 30 / std::max(size.width, size.height);
2422 [icon_ zoomToScale:scale];
2425 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2427 [name_ setText:[package name]];
2428 [description_ setText:[package tagline]];
2433 if (source != nil) {
2434 label = [source label];
2435 trusted = [source trusted];
2436 } else if ([[package id] isEqualToString:@"firmware"]) {
2440 label = @"Unknown/Local";
2444 [source_ setText:[NSString stringWithFormat:@"from %@ (%@)", label, Simplify([package section])]];
2447 [self addSubview:trusted_];
2449 [trusted_ removeFromSuperview];
2452 - (void) _setSelected:(float)fraction {
2453 CGColor black(space_,
2454 Interpolate(0.0, 1.0, fraction),
2455 Interpolate(0.0, 1.0, fraction),
2456 Interpolate(0.0, 1.0, fraction),
2459 CGColor gray(space_,
2460 Interpolate(0.4, 1.0, fraction),
2461 Interpolate(0.4, 1.0, fraction),
2462 Interpolate(0.4, 1.0, fraction),
2465 [name_ setColor:black];
2466 [description_ setColor:gray];
2467 [source_ setColor:black];
2470 - (void) setSelected:(BOOL)selected {
2471 [self _setSelected:(selected ? 1.0 : 0.0)];
2472 [super setSelected:selected];
2475 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2477 [self _setSelected:(selected ? 1.0 : 0.0)];
2478 [super setSelected:selected withFade:fade];
2481 - (void) _setSelectionFadeFraction:(float)fraction {
2482 [self _setSelected:fraction];
2483 [super _setSelectionFadeFraction:fraction];
2488 /* Section Cell {{{ */
2489 @interface SectionCell : UITableCell {
2491 UITextLabel *count_;
2495 - (void) setSection:(Section *)section;
2497 - (void) _setSelected:(float)fraction;
2498 - (void) setSelected:(BOOL)selected;
2499 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2500 - (void) _setSelectionFadeFraction:(float)fraction;
2504 @implementation SectionCell
2513 if ((self = [super init]) != nil) {
2514 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2515 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2517 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2518 [name_ setBackgroundColor:Clear_];
2519 [name_ setFont:bold];
2521 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2522 [count_ setCentersHorizontally:YES];
2523 [count_ setBackgroundColor:Clear_];
2524 [count_ setFont:small];
2525 [count_ setColor:White_];
2527 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2528 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2530 [self addSubview:folder];
2531 [self addSubview:name_];
2532 [self addSubview:count_];
2534 [self _setSelected:0];
2541 - (void) setSection:(Section *)section {
2542 if (section == nil) {
2543 [name_ setText:@"All Packages"];
2544 [count_ setText:nil];
2546 NSString *name = [section name];
2547 [name_ setText:(name == nil ? @"(No Section)" : name)];
2548 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2552 - (void) _setSelected:(float)fraction {
2553 CGColor black(space_,
2554 Interpolate(0.0, 1.0, fraction),
2555 Interpolate(0.0, 1.0, fraction),
2556 Interpolate(0.0, 1.0, fraction),
2559 [name_ setColor:black];
2562 - (void) setSelected:(BOOL)selected {
2563 [self _setSelected:(selected ? 1.0 : 0.0)];
2564 [super setSelected:selected];
2567 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2569 [self _setSelected:(selected ? 1.0 : 0.0)];
2570 [super setSelected:selected withFade:fade];
2573 - (void) _setSelectionFadeFraction:(float)fraction {
2574 [self _setSelected:fraction];
2575 [super _setSelectionFadeFraction:fraction];
2581 /* Browser Interface {{{ */
2582 @interface BrowserView : RVPage {
2583 _transient Database *database_;
2584 UIScroller *scroller_;
2585 UIWebView *webview_;
2586 NSMutableArray *urls_;
2587 UIProgressIndicator *indicator_;
2594 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2595 - (void) loadURL:(NSURL *)url;
2597 - (void) loadRequest:(NSURLRequest *)request;
2600 - (WebView *) webView;
2602 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2607 /* Package View {{{ */
2608 @protocol PackageViewDelegate
2609 - (void) performPackage:(Package *)package;
2612 @interface PackageView : RVPage {
2613 _transient Database *database_;
2614 UIPreferencesTable *table_;
2617 UITextView *description_;
2620 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2621 - (void) setPackage:(Package *)package;
2625 @implementation PackageView
2628 [table_ setDataSource:nil];
2629 [table_ setDelegate:nil];
2631 if (package_ != nil)
2635 if (description_ != nil)
2636 [description_ release];
2641 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2642 return [package_ source] == nil ? 2 : 3;
2645 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2648 case 1: return @"Package Details";
2649 case 2: return @"Source Information";
2651 default: _assert(false);
2655 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2656 if (group != 0 || row != 1)
2659 return [description_ visibleTextRect].size.height + TextViewOffset_;
2662 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2664 case 0: return [package_ website] == nil ? 2 : 3;
2668 default: _assert(false);
2672 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2673 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2674 [cell setShowSelection:NO];
2677 case 0: switch (row) {
2679 [cell setTitle:[package_ name]];
2680 [cell setValue:[package_ latest]];
2684 [cell addSubview:description_];
2688 [cell setTitle:@"More Information"];
2689 [cell setShowDisclosure:YES];
2690 [cell setShowSelection:YES];
2693 default: _assert(false);
2696 case 1: switch (row) {
2698 [cell setTitle:@"Identifier"];
2699 [cell setValue:[package_ id]];
2703 [cell setTitle:@"Installed Version"];
2704 NSString *installed([package_ installed]);
2705 [cell setValue:(installed == nil ? @"n/a" : installed)];
2709 [cell setTitle:@"Section"];
2710 NSString *section([package_ section]);
2711 [cell setValue:(section == nil ? @"n/a" : section)];
2715 [cell setTitle:@"Expanded Size"];
2716 [cell setValue:SizeString([package_ size])];
2720 [cell setTitle:@"Maintainer"];
2721 [cell setValue:[[package_ maintainer] name]];
2722 [cell setShowDisclosure:YES];
2723 [cell setShowSelection:YES];
2726 default: _assert(false);
2729 case 2: switch (row) {
2731 [cell setTitle:[[package_ source] label]];
2732 [cell setValue:[[package_ source] version]];
2736 [cell setValue:[[package_ source] description]];
2740 [cell setTitle:@"Origin"];
2741 [cell setValue:[[package_ source] origin]];
2744 default: _assert(false);
2747 default: _assert(false);
2753 - (BOOL) canSelectRow:(int)row {
2757 - (void) tableRowSelected:(NSNotification *)notification {
2758 int row = [table_ selectedRow];
2759 NSString *website = [package_ website];
2761 if (row == (website == nil ? 8 : 9))
2762 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2763 [[package_ maintainer] email],
2764 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2766 else if (website != nil && row == 3) {
2767 NSURL *url = [NSURL URLWithString:website];
2768 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2769 [browser setDelegate:delegate_];
2770 [book_ pushPage:browser];
2771 [browser loadURL:url];
2775 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2777 case 1: [delegate_ installPackage:package_]; break;
2778 case 2: [delegate_ removePackage:package_]; break;
2784 - (void) _rightButtonClicked {
2785 if ([package_ installed] == nil)
2786 [delegate_ installPackage:package_];
2788 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2790 if ([package_ upgradable])
2791 [buttons addObject:@"Upgrade"];
2793 [buttons addObject:@"Reinstall"];
2795 [buttons addObject:@"Remove"];
2796 [buttons addObject:@"Cancel"];
2798 [delegate_ slideUp:[[[UIAlertSheet alloc]
2799 initWithTitle:@"Manage Package"
2801 defaultButtonIndex:2
2808 - (NSString *) rightButtonTitle {
2809 _assert(package_ != nil);
2810 return [package_ installed] == nil ? @"Install" : @"Modify";
2813 - (NSString *) title {
2817 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2818 if ((self = [super initWithBook:book]) != nil) {
2819 database_ = database;
2821 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2822 [self addSubview:table_];
2824 [table_ setDataSource:self];
2825 [table_ setDelegate:self];
2829 - (void) setPackage:(Package *)package {
2830 if (package_ != nil) {
2831 [package_ autorelease];
2840 if (description_ != nil) {
2841 [description_ release];
2845 if (package != nil) {
2846 package_ = [package retain];
2847 name_ = [[package id] retain];
2849 NSString *description([package description]);
2850 if (description == nil)
2851 description = [package tagline];
2852 description_ = [GetTextView(description, 12, true) retain];
2854 [description_ setTextColor:Black_];
2856 [table_ reloadData];
2860 - (void) resetViewAnimated:(BOOL)animated {
2861 [table_ resetViewAnimated:animated];
2864 - (void) reloadData {
2865 [self setPackage:[database_ packageWithName:name_]];
2866 [self reloadButtons];
2871 /* Package Table {{{ */
2872 @interface PackageTable : RVPage {
2873 _transient Database *database_;
2877 NSMutableArray *packages_;
2878 NSMutableArray *sections_;
2879 UISectionList *list_;
2882 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2884 - (void) setDelegate:(id)delegate;
2885 - (void) setObject:(id)object;
2887 - (void) reloadData;
2888 - (void) resetCursor;
2892 @implementation PackageTable
2895 [list_ setDataSource:nil];
2900 [packages_ release];
2901 [sections_ release];
2906 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2907 return [sections_ count];
2910 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2911 return [[sections_ objectAtIndex:section] name];
2914 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2915 return [[sections_ objectAtIndex:section] row];
2918 - (int) numberOfRowsInTable:(UITable *)table {
2919 return [packages_ count];
2922 - (float) table:(UITable *)table heightForRow:(int)row {
2926 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2928 reusing = [[[PackageCell alloc] init] autorelease];
2929 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2933 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2937 - (void) tableRowSelected:(NSNotification *)notification {
2938 int row = [[notification object] selectedRow];
2942 Package *package = [packages_ objectAtIndex:row];
2943 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2944 [view setDelegate:delegate_];
2945 [view setPackage:package];
2946 [book_ pushPage:view];
2949 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2950 if ((self = [super initWithBook:book]) != nil) {
2951 database_ = database;
2952 title_ = [title retain];
2954 object_ = object == nil ? nil : [object retain];
2956 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2957 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2959 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2960 [list_ setDataSource:self];
2962 UITableColumn *column = [[[UITableColumn alloc]
2963 initWithTitle:@"Name"
2965 width:[self frame].size.width
2968 UITable *table = [list_ table];
2969 [table setSeparatorStyle:1];
2970 [table addTableColumn:column];
2971 [table setDelegate:self];
2972 [table setReusesTableCells:YES];
2974 [self addSubview:list_];
2979 - (void) setDelegate:(id)delegate {
2980 delegate_ = delegate;
2983 - (void) setObject:(id)object {
2989 object_ = [object retain];
2992 - (void) reloadData {
2993 NSArray *packages = [database_ packages];
2995 [packages_ removeAllObjects];
2996 [sections_ removeAllObjects];
2998 for (size_t i(0); i != [packages count]; ++i) {
2999 Package *package([packages objectAtIndex:i]);
3000 if ([[package performSelector:filter_ withObject:object_] boolValue])
3001 [packages_ addObject:package];
3004 [packages_ sortUsingSelector:@selector(compareByName:)];
3006 Section *section = nil;
3008 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3009 Package *package = [packages_ objectAtIndex:offset];
3010 NSString *name = [package index];
3012 if (section == nil || ![[section name] isEqualToString:name]) {
3013 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3014 [sections_ addObject:section];
3017 [section addToCount];
3023 - (NSString *) title {
3027 - (void) resetViewAnimated:(BOOL)animated {
3028 [list_ resetViewAnimated:animated];
3031 - (void) resetCursor {
3032 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3038 /* Browser Implementation {{{ */
3039 @implementation BrowserView
3042 WebView *webview = [webview_ webView];
3043 [webview setFrameLoadDelegate:nil];
3044 [webview setResourceLoadDelegate:nil];
3045 [webview setUIDelegate:nil];
3047 [scroller_ setDelegate:nil];
3048 [webview_ setDelegate:nil];
3050 [scroller_ release];
3053 [indicator_ release];
3059 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3060 NSMutableURLRequest *request = [NSMutableURLRequest
3063 timeoutInterval:30.0
3066 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3067 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3068 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3070 [self loadRequest:request];
3074 - (void) loadURL:(NSURL *)url {
3075 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3078 // XXX: this needs to add the headers
3079 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3083 - (void) loadRequest:(NSURLRequest *)request {
3084 [webview_ loadRequest:request];
3087 - (void) reloadURL {
3088 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3089 [urls_ removeLastObject];
3090 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3093 - (WebView *) webView {
3094 return [webview_ webView];
3097 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3098 [scroller_ setContentSize:frame.size];
3101 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3102 [self view:sender didSetFrame:frame];
3105 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3106 return [self _addHeadersToRequest:request];
3109 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3110 if ([[[request URL] scheme] isEqualToString:@"apptapp"])
3112 [self setBackButtonTitle:title_];
3113 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3114 [browser setDelegate:delegate_];
3115 [book_ pushPage:browser];
3116 [browser loadRequest:[self _addHeadersToRequest:request]];
3117 return [browser webView];
3120 - (void) webView:(WebView *)sender willClickElement:(id)element {
3121 if (![element respondsToSelector:@selector(href)])
3123 NSString *href = [element href];
3126 if ([href hasPrefix:@"apptapp://package/"]) {
3127 NSString *name = [href substringFromIndex:18];
3128 Package *package = [database_ packageWithName:name];
3129 if (package == nil) {
3130 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3131 initWithTitle:@"Cannot Locate Package"
3132 buttons:[NSArray arrayWithObjects:@"Close", nil]
3133 defaultButtonIndex:0
3138 [sheet setBodyText:[NSString stringWithFormat:
3139 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
3142 [sheet popupAlertAnimated:YES];
3144 [self setBackButtonTitle:title_];
3145 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3146 [view setDelegate:delegate_];
3147 [view setPackage:package];
3148 [book_ pushPage:view];
3153 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3154 title_ = [title retain];
3155 [self setTitle:title];
3158 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3159 if ([frame parentFrame] != nil)
3164 [indicator_ startAnimation];
3165 [self reloadButtons];
3167 if (title_ != nil) {
3172 [self setTitle:@"Loading..."];
3174 WebView *webview = [webview_ webView];
3175 NSString *href = [webview mainFrameURL];
3176 [urls_ addObject:[NSURL URLWithString:href]];
3178 CGRect webrect = [scroller_ frame];
3179 webrect.size.height = 0;
3180 [webview_ setFrame:webrect];
3183 - (void) _finishLoading {
3186 [indicator_ stopAnimation];
3187 [self reloadButtons];
3191 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3192 if ([frame parentFrame] != nil)
3194 [self _finishLoading];
3197 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3198 if ([frame parentFrame] != nil)
3200 [self setTitle:[error localizedDescription]];
3201 [self _finishLoading];
3204 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3205 if ((self = [super initWithBook:book]) != nil) {
3206 database_ = database;
3209 struct CGRect bounds = [self bounds];
3211 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3212 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3213 [self addSubview:pinstripe];
3215 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3216 [self addSubview:scroller_];
3218 [scroller_ setScrollingEnabled:YES];
3219 [scroller_ setAdjustForContentSizeChange:YES];
3220 [scroller_ setClipsSubviews:YES];
3221 [scroller_ setAllowsRubberBanding:YES];
3222 [scroller_ setScrollDecelerationFactor:0.99];
3223 [scroller_ setDelegate:self];
3225 CGRect webrect = [scroller_ bounds];
3226 webrect.size.height = 0;
3228 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3229 [scroller_ addSubview:webview_];
3231 [webview_ setTilingEnabled:YES];
3232 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3233 [webview_ setAutoresizes:YES];
3234 [webview_ setDelegate:self];
3235 //[webview_ setEnabledGestures:2];
3237 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3238 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3239 [indicator_ setStyle:0];
3241 Package *package([database_ packageWithName:@"cydia"]);
3242 NSString *application = package == nil ? @"Cydia" : [NSString
3243 stringWithFormat:@"Cydia/%@",
3247 WebView *webview = [webview_ webView];
3248 [webview setApplicationNameForUserAgent:application];
3249 [webview setFrameLoadDelegate:self];
3250 [webview setResourceLoadDelegate:self];
3251 [webview setUIDelegate:self];
3253 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3257 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3261 - (void) _leftButtonClicked {
3262 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3263 initWithTitle:@"About Cydia Packager"
3264 buttons:[NSArray arrayWithObjects:@"Close", nil]
3265 defaultButtonIndex:0
3271 @"Copyright (C) 2008\n"
3272 "Jay Freeman (saurik)\n"
3273 "saurik@saurik.com\n"
3274 "http://www.saurik.com/\n"
3277 "http://www.theokorigroup.com/\n"
3279 "College of Creative Studies,\n"
3280 "University of California,\n"
3282 "http://www.ccs.ucsb.edu/"
3285 [sheet popupAlertAnimated:YES];
3288 - (void) _rightButtonClicked {
3293 - (NSString *) leftButtonTitle {
3297 - (NSString *) rightButtonTitle {
3298 return loading_ ? @"" : @"Reload";
3301 - (NSString *) title {
3305 - (NSString *) backButtonTitle {
3309 - (void) setPageActive:(BOOL)active {
3311 [book_ addSubview:indicator_];
3313 [indicator_ removeFromSuperview];
3316 - (void) resetViewAnimated:(BOOL)animated {
3322 @interface CYBook : RVBook <
3325 _transient Database *database_;
3327 UIProgressIndicator *indicator_;
3328 UITextLabel *prompt_;
3329 UIProgressBar *progress_;
3333 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3339 /* Install View {{{ */
3340 @interface InstallView : RVPage {
3341 _transient Database *database_;
3342 NSMutableArray *packages_;
3343 NSMutableArray *sections_;
3347 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3348 - (void) reloadData;
3352 @implementation InstallView
3355 [list_ setDataSource:nil];
3356 [list_ setDelegate:nil];
3358 [packages_ release];
3359 [sections_ release];
3364 - (int) numberOfRowsInTable:(UITable *)table {
3365 return [sections_ count] + 1;
3368 - (float) table:(UITable *)table heightForRow:(int)row {
3372 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3374 reusing = [[[SectionCell alloc] init] autorelease];
3375 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3379 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3383 - (void) tableRowSelected:(NSNotification *)notification {
3384 int row = [[notification object] selectedRow];
3393 title = @"All Packages";
3395 section = [sections_ objectAtIndex:(row - 1)];
3396 title = [section name];
3399 PackageTable *table = [[[PackageTable alloc]
3403 filter:@selector(isUninstalledInSection:)
3404 with:(section == nil ? nil : [section name])
3407 [table setDelegate:delegate_];
3409 [book_ pushPage:table];
3412 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3413 if ((self = [super initWithBook:book]) != nil) {
3414 database_ = database;
3416 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3417 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3419 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3420 [self addSubview:list_];
3422 UITableColumn *column = [[[UITableColumn alloc]
3423 initWithTitle:@"Name"
3425 width:[self frame].size.width
3428 [list_ setDataSource:self];
3429 [list_ setSeparatorStyle:1];
3430 [list_ addTableColumn:column];
3431 [list_ setDelegate:self];
3432 [list_ setReusesTableCells:YES];
3438 - (void) reloadData {
3439 NSArray *packages = [database_ packages];
3441 [packages_ removeAllObjects];
3442 [sections_ removeAllObjects];
3444 for (size_t i(0); i != [packages count]; ++i) {
3445 Package *package([packages objectAtIndex:i]);
3446 if ([package installed] == nil)
3447 [packages_ addObject:package];
3450 [packages_ sortUsingSelector:@selector(compareBySection:)];
3452 Section *section = nil;
3453 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3454 Package *package = [packages_ objectAtIndex:offset];
3455 NSString *name = [package section];
3457 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3458 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3459 [sections_ addObject:section];
3462 [section addToCount];
3468 - (void) resetViewAnimated:(BOOL)animated {
3469 [list_ resetViewAnimated:animated];
3472 - (NSString *) title {
3476 - (NSString *) backButtonTitle {
3482 /* Changes View {{{ */
3483 @interface ChangesView : RVPage {
3484 _transient Database *database_;
3485 NSMutableArray *packages_;
3486 NSMutableArray *sections_;
3487 UISectionList *list_;
3491 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3492 - (void) reloadData;
3496 @implementation ChangesView
3499 [[list_ table] setDelegate:nil];
3500 [list_ setDataSource:nil];
3502 [packages_ release];
3503 [sections_ release];
3508 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3509 return [sections_ count];
3512 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3513 return [[sections_ objectAtIndex:section] name];
3516 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3517 return [[sections_ objectAtIndex:section] row];
3520 - (int) numberOfRowsInTable:(UITable *)table {
3521 return [packages_ count];
3524 - (float) table:(UITable *)table heightForRow:(int)row {
3528 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3530 reusing = [[[PackageCell alloc] init] autorelease];
3531 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3535 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3539 - (void) tableRowSelected:(NSNotification *)notification {
3540 int row = [[notification object] selectedRow];
3543 Package *package = [packages_ objectAtIndex:row];
3544 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3545 [view setDelegate:delegate_];
3546 [view setPackage:package];
3547 [book_ pushPage:view];
3550 - (void) _leftButtonClicked {
3551 [(CYBook *)book_ update];
3552 [self reloadButtons];
3555 - (void) _rightButtonClicked {
3556 [delegate_ distUpgrade];
3559 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3560 if ((self = [super initWithBook:book]) != nil) {
3561 database_ = database;
3563 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3564 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3566 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3567 [self addSubview:list_];
3569 [list_ setShouldHideHeaderInShortLists:NO];
3570 [list_ setDataSource:self];
3571 //[list_ setSectionListStyle:1];
3573 UITableColumn *column = [[[UITableColumn alloc]
3574 initWithTitle:@"Name"
3576 width:[self frame].size.width
3579 UITable *table = [list_ table];
3580 [table setSeparatorStyle:1];
3581 [table addTableColumn:column];
3582 [table setDelegate:self];
3583 [table setReusesTableCells:YES];
3589 - (void) reloadData {
3590 NSArray *packages = [database_ packages];
3592 [packages_ removeAllObjects];
3593 [sections_ removeAllObjects];
3595 for (size_t i(0); i != [packages count]; ++i) {
3596 Package *package([packages objectAtIndex:i]);
3597 if ([package installed] == nil || [package upgradable])
3598 [packages_ addObject:package];
3601 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3603 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3604 Section *section = nil;
3607 bool unseens = false;
3609 CFLocaleRef locale = CFLocaleCopyCurrent();
3610 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3612 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3613 Package *package = [packages_ objectAtIndex:offset];
3615 if ([package upgradable]) {
3617 [upgradable addToCount];
3620 NSDate *seen = [package seen];
3625 name = [@"n/a ?" retain];
3627 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3630 if (section == nil || ![[section name] isEqualToString:name]) {
3631 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3632 [sections_ addObject:section];
3636 [section addToCount];
3640 CFRelease(formatter);
3644 Section *last = [sections_ lastObject];
3645 size_t count = [last count];
3646 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3647 [sections_ removeLastObject];
3651 [sections_ insertObject:upgradable atIndex:0];
3654 [self reloadButtons];
3657 - (void) resetViewAnimated:(BOOL)animated {
3658 [list_ resetViewAnimated:animated];
3661 - (NSString *) leftButtonTitle {
3662 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3665 - (NSString *) rightButtonTitle {
3666 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3669 - (NSString *) title {
3675 /* Manage View {{{ */
3676 @interface ManageView : PackageTable {
3679 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3683 @implementation ManageView
3685 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3690 filter:@selector(isInstalledInSection:)
3696 - (NSString *) title {
3697 return @"Installed Packages";
3700 - (NSString *) backButtonTitle {
3701 return @"All Packages";
3706 /* Search View {{{ */
3707 @protocol SearchViewDelegate
3708 - (void) showKeyboard:(BOOL)show;
3711 @interface SearchView : RVPage {
3713 UISearchField *field_;
3714 UITransitionView *transition_;
3715 PackageTable *table_;
3716 UIPreferencesTable *advanced_;
3721 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3722 - (void) reloadData;
3726 @implementation SearchView
3730 [[field_ textTraits] setEditingDelegate:nil];
3732 [field_ setDelegate:nil];
3734 [accessory_ release];
3736 [transition_ release];
3738 [advanced_ release];
3743 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3744 [delegate_ showKeyboard:YES];
3745 [table_ setEnabled:NO];
3746 [self addSubview:dimmed_];
3749 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3750 [dimmed_ removeFromSuperview];
3751 [table_ setEnabled:YES];
3752 [delegate_ showKeyboard:NO];
3755 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3756 NSString *text([field_ text]);
3757 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3760 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3761 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3765 [field_ resignFirstResponder];
3769 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3770 if ((self = [super initWithBook:book]) != nil) {
3771 CGRect pageBounds = [book_ pageBounds];
3773 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
3774 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3775 [self addSubview:pinstripe];*/
3777 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3778 CGColor dimmed(space_, 0, 0, 0, 0.5);
3779 [dimmed_ setBackgroundColor:dimmed];
3781 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3782 [self addSubview:transition_];
3784 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3786 table_ = [[PackageTable alloc]
3790 filter:@selector(isSearchedForBy:)
3794 [transition_ transition:0 toView:table_];
3796 CGRect cnfrect = {{1, 38}, {17, 18}};
3799 area.origin.x = cnfrect.size.width + 15;
3801 area.size.width = [self bounds].size.width - area.origin.x - 18;
3802 area.size.height = [UISearchField defaultHeight];
3804 field_ = [[UISearchField alloc] initWithFrame:area];
3806 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3807 [field_ setFont:font];
3810 [field_ setPlaceholder:@"Package Names & Descriptions"];
3811 [field_ setPaddingTop:5];
3812 [field_ setDelegate:self];
3815 UITextTraits *traits = [field_ textTraits];
3816 [traits setEditingDelegate:self];
3817 [traits setReturnKeyType:6];
3818 [traits setAutoCapsType:0];
3819 [traits setAutoCorrectionType:1];
3822 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3823 [configure setShowPressFeedback:YES];
3824 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
3825 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3827 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3828 [accessory_ addSubview:field_];
3829 [accessory_ addSubview:configure];
3834 LKAnimation *animation = [LKTransition animation];
3835 [animation setType:@"oglFlip"];
3836 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3837 [animation setFillMode:@"extended"];
3838 [animation setTransitionFlags:3];
3839 [animation setDuration:10];
3840 [animation setSpeed:0.35];
3841 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
3842 [[transition_ _layer] addAnimation:animation forKey:0];
3843 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3844 flipped_ = !flipped_;
3847 - (void) configurePushed {
3848 [field_ resignFirstResponder];
3852 - (void) resetViewAnimated:(BOOL)animated {
3855 [table_ resetViewAnimated:animated];
3858 - (void) reloadData {
3861 [table_ setObject:[field_ text]];
3862 [table_ reloadData];
3863 [table_ resetCursor];
3866 - (UIView *) accessoryView {
3870 - (NSString *) title {
3874 - (NSString *) backButtonTitle {
3878 - (void) setDelegate:(id)delegate {
3879 [table_ setDelegate:delegate];
3880 [super setDelegate:delegate];
3886 @implementation CYBook
3890 [indicator_ release];
3892 [progress_ release];
3901 [navbar_ setPrompt:@""];
3902 [navbar_ addSubview:overlay_];
3903 [indicator_ startAnimation];
3904 [prompt_ setText:@"Updating Database..."];
3905 [progress_ setProgress:0];
3910 detachNewThreadSelector:@selector(_update)
3919 [overlay_ removeFromSuperview];
3920 [indicator_ stopAnimation];
3921 [delegate_ reloadData];
3923 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3926 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3927 if ((self = [super initWithFrame:frame]) != nil) {
3928 database_ = database;
3930 CGRect ovrrect = [navbar_ bounds];
3931 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3933 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3935 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3936 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3937 CGRect indrect = {{indoffset, indoffset}, indsize};
3939 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3940 [indicator_ setStyle:2];
3941 [overlay_ addSubview:indicator_];
3943 CGSize prmsize = {200, indsize.width};
3946 indoffset * 2 + indsize.width,
3947 (ovrrect.size.height - prmsize.height) / 2
3950 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3952 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3954 [prompt_ setColor:White_];
3955 [prompt_ setBackgroundColor:Clear_];
3956 [prompt_ setFont:font];
3960 [overlay_ addSubview:prompt_];
3962 CGSize prgsize = {75, 100};
3965 ovrrect.size.width - prgsize.width - 10,
3966 (ovrrect.size.height - prgsize.height) / 2
3969 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3970 [progress_ setStyle:0];
3971 [overlay_ addSubview:progress_];
3976 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3979 status.setDelegate(self);
3981 [database_ updateWithStatus:status];
3984 performSelectorOnMainThread:@selector(_update_)
3992 - (void) setProgressError:(NSString *)error {
3994 performSelectorOnMainThread:@selector(_setProgressError:)
4000 - (void) setProgressTitle:(NSString *)title {
4002 performSelectorOnMainThread:@selector(_setProgressTitle:)
4008 - (void) setProgressPercent:(float)percent {
4011 - (void) addProgressOutput:(NSString *)output {
4013 performSelectorOnMainThread:@selector(_addProgressOutput:)
4019 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4023 - (void) _setProgressError:(NSString *)error {
4024 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4027 - (void) _setProgressTitle:(NSString *)title {
4028 [prompt_ setText:[title stringByAppendingString:@"..."]];
4031 - (void) _addProgressOutput:(NSString *)output {
4036 @interface Cydia : UIApplication <
4037 ConfirmationViewDelegate,
4038 ProgressViewDelegate,
4047 UIButtonBar *buttonbar_;
4049 ConfirmationView *confirm_;
4051 Database *database_;
4052 ProgressView *progress_;
4056 UIKeyboard *keyboard_;
4061 @implementation Cydia
4063 - (void) _reloadData {
4064 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4065 [hud setText:@"Reloading Data"];
4066 [overlay_ addSubview:hud];
4069 [database_ reloadData];
4073 if (Packages_ == nil) {
4074 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
4075 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4080 NSArray *packages = [database_ packages];
4081 for (int i(0), e([packages count]); i != e; ++i) {
4082 Package *package = [packages objectAtIndex:i];
4083 if ([package upgradable])
4088 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4089 [buttonbar_ setBadgeValue:badge forButton:3];
4090 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4091 [buttonbar_ setBadgeAnimated:YES forButton:3];
4092 [self setApplicationBadge:badge];
4094 [buttonbar_ setBadgeValue:nil forButton:3];
4095 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4096 [buttonbar_ setBadgeAnimated:NO forButton:3];
4097 [self removeApplicationBadge];
4100 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4104 [hud removeFromSuperview];*/
4107 - (void) reloadData {
4108 @synchronized (self) {
4109 if (confirm_ == nil)
4115 pkgProblemResolver *resolver = [database_ resolver];
4117 resolver->InstallProtect();
4118 if (!resolver->Resolve(true))
4123 [database_ prepare];
4125 if ([database_ cache]->BrokenCount() == 0)
4126 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4128 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4129 NSArray *packages = [database_ packages];
4131 for (size_t i(0); i != [packages count]; ++i) {
4132 Package *package = [packages objectAtIndex:i];
4133 if ([package broken])
4134 [broken addObject:[package name]];
4137 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4138 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4139 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4140 defaultButtonIndex:0
4145 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4146 [sheet popupAlertAnimated:YES];
4152 - (void) installPackage:(Package *)package {
4153 @synchronized (self) {
4160 - (void) removePackage:(Package *)package {
4161 @synchronized (self) {
4168 - (void) distUpgrade {
4169 @synchronized (self) {
4170 [database_ upgrade];
4176 @synchronized (self) {
4184 [overlay_ removeFromSuperview];
4188 detachNewThreadSelector:@selector(perform)
4195 - (void) bootstrap_ {
4197 [database_ upgrade];
4198 [database_ prepare];
4199 [database_ perform];
4202 - (void) bootstrap {
4204 detachNewThreadSelector:@selector(bootstrap_)
4207 title:@"Bootstrap Install..."
4211 - (void) progressViewIsComplete:(ProgressView *)progress {
4212 @synchronized (self) {
4215 if (confirm_ != nil) {
4216 [underlay_ addSubview:overlay_];
4217 [confirm_ removeFromSuperview];
4224 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4228 - (void) setPage:(RVPage *)page {
4229 [page setDelegate:self];
4230 [book_ setPage:page];
4233 - (RVPage *) _setNewsPage {
4234 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4235 [self setPage:browser];
4236 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4240 - (void) buttonBarItemTapped:(id)sender {
4241 unsigned tag = [sender tag];
4243 [book_ resetViewAnimated:YES];
4249 [self _setNewsPage];
4253 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4257 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4261 [self setPage:[[[ManageView alloc] initWithBook:book_ database:database_] autorelease]];
4265 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4275 - (void) applicationWillSuspend {
4276 [super applicationWillSuspend];
4279 if (FW_LEAST(1,1,3))
4280 notify_post("com.apple.language.changed");
4282 system("launchctl stop com.apple.SpringBoard");
4285 - (void) applicationDidFinishLaunching:(id)unused {
4286 _assert(pkgInitConfig(*_config));
4287 _assert(pkgInitSystem(*_config, _system));
4292 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4293 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4295 [window_ orderFront: self];
4296 [window_ makeKey: self];
4297 [window_ _setHidden: NO];
4299 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
4300 [window_ setContentView:progress_];
4302 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4303 [progress_ setContentView:underlay_];
4305 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4308 [underlay_ addSubview:overlay_];
4310 database_ = [[Database alloc] init];
4311 [database_ setDelegate:progress_];
4313 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4314 0, 0, screenrect.size.width, screenrect.size.height - 48
4315 ) database:database_];
4317 [book_ setDelegate:self];
4319 [overlay_ addSubview:book_];
4321 NSArray *buttonitems = [NSArray arrayWithObjects:
4322 [NSDictionary dictionaryWithObjectsAndKeys:
4323 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4324 @"news-up.png", kUIButtonBarButtonInfo,
4325 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4326 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4327 self, kUIButtonBarButtonTarget,
4328 @"News", kUIButtonBarButtonTitle,
4329 @"0", kUIButtonBarButtonType,
4332 [NSDictionary dictionaryWithObjectsAndKeys:
4333 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4334 @"install-up.png", kUIButtonBarButtonInfo,
4335 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4336 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4337 self, kUIButtonBarButtonTarget,
4338 @"Install", kUIButtonBarButtonTitle,
4339 @"0", kUIButtonBarButtonType,
4342 [NSDictionary dictionaryWithObjectsAndKeys:
4343 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4344 @"changes-up.png", kUIButtonBarButtonInfo,
4345 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4346 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4347 self, kUIButtonBarButtonTarget,
4348 @"Changes", kUIButtonBarButtonTitle,
4349 @"0", kUIButtonBarButtonType,
4352 [NSDictionary dictionaryWithObjectsAndKeys:
4353 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4354 @"manage-up.png", kUIButtonBarButtonInfo,
4355 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4356 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4357 self, kUIButtonBarButtonTarget,
4358 @"Manage", kUIButtonBarButtonTitle,
4359 @"0", kUIButtonBarButtonType,
4362 [NSDictionary dictionaryWithObjectsAndKeys:
4363 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4364 @"search-up.png", kUIButtonBarButtonInfo,
4365 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4366 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4367 self, kUIButtonBarButtonTarget,
4368 @"Search", kUIButtonBarButtonTitle,
4369 @"0", kUIButtonBarButtonType,
4373 buttonbar_ = [[UIButtonBar alloc]
4375 withFrame:CGRectMake(
4376 0, screenrect.size.height - 48,
4377 screenrect.size.width, 48
4379 withItemList:buttonitems
4382 [buttonbar_ setDelegate:self];
4383 [buttonbar_ setBarStyle:1];
4384 [buttonbar_ setButtonBarTrackingMode:2];
4386 int buttons[5] = {1, 2, 3, 4, 5};
4387 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4388 [buttonbar_ showButtonGroup:0 withDuration:0];
4390 for (int i = 0; i != 5; ++i)
4391 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4392 i * 64 + 2, 1, 60, 48
4395 [buttonbar_ showSelectionForButton:1];
4396 [overlay_ addSubview:buttonbar_];
4398 [UIKeyboard initImplementationNow];
4399 CGSize keysize = [UIKeyboard defaultSize];
4400 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4401 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4402 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4407 [progress_ resetView];
4412 [self _setNewsPage];
4415 - (void) showKeyboard:(BOOL)show {
4417 [overlay_ addSubview:keyboard_];
4419 [keyboard_ removeFromSuperview];
4422 - (void) slideUp:(UIAlertSheet *)alert {
4423 [alert presentSheetFromButtonBar:buttonbar_];
4428 void AddPreferences(NSString *plist) {
4429 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4431 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4432 _assert(settings != NULL);
4433 NSMutableArray *items = [settings objectForKey:@"items"];
4437 for (size_t i(0); i != [items count]; ++i) {
4438 NSMutableDictionary *item([items objectAtIndex:i]);
4439 NSString *label = [item objectForKey:@"label"];
4440 if (label != nil && [label isEqualToString:@"Cydia"]) {
4447 for (size_t i(0); i != [items count]; ++i) {
4448 NSDictionary *item([items objectAtIndex:i]);
4449 NSString *label = [item objectForKey:@"label"];
4450 if (label != nil && [label isEqualToString:@"General"]) {
4451 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4452 @"CydiaSettings", @"bundle",
4453 @"PSLinkCell", @"cell",
4454 [NSNumber numberWithBool:YES], @"hasIcon",
4455 [NSNumber numberWithBool:YES], @"isController",
4457 nil] atIndex:(i + 1)];
4463 _assert([settings writeToFile:plist atomically:YES] == YES);
4470 id Alloc_(id self, SEL selector) {
4471 id object = alloc_(self, selector);
4472 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4477 id Dealloc_(id self, SEL selector) {
4478 id object = dealloc_(self, selector);
4479 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4483 int main(int argc, char *argv[]) {
4484 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4486 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4488 Home_ = NSHomeDirectory();
4491 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4492 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4493 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4494 Sounds_Keyboard_ = [keyboard boolValue];
4500 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4501 alloc_ = alloc->method_imp;
4502 alloc->method_imp = (IMP) &Alloc_;*/
4504 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4505 dealloc_ = dealloc->method_imp;
4506 dealloc->method_imp = (IMP) &Dealloc_;*/
4508 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4509 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4510 Firmware_ = strdup([prover UTF8String]);
4511 NSArray *versions = [prover componentsSeparatedByString:@"."];
4512 int count = [versions count];
4513 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4514 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4515 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4520 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4521 char *machine = new char[size];
4522 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4525 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4526 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4527 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4528 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4532 IOObjectRelease(service);
4535 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4536 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4538 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4539 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4541 Packages_ = [Metadata_ objectForKey:@"Packages"];
4543 setenv("CYDIA", "", _not(int));
4544 if (access("/User", F_OK) != 0)
4545 system("/usr/libexec/cydia/firmware.sh");
4546 system("dpkg --configure -a");
4548 space_ = CGColorSpaceCreateDeviceRGB();
4550 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4551 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4552 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4553 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4555 int value = UIApplicationMain(argc, argv, [Cydia class]);
4557 CGColorSpaceRelease(space_);