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;
121 #define $_ fprintf(stderr, "+");_trace();
122 #define _$ fprintf(stderr, "-");_trace();
125 /* iPhoneOS 2.0 Compatibility {{{ */
127 @interface UICGColor : NSObject {
130 - (id) initWithCGColor:(CGColorRef)color;
136 - (UIFont *) fontWithSize:(CGFloat)size;
139 @interface NSObject (iPhoneOS)
140 - (CGColorRef) cgColor;
141 - (CGColorRef) CGColor;
145 @implementation NSObject (iPhoneOS)
147 - (CGColorRef) cgColor {
148 return [self CGColor];
151 - (CGColorRef) CGColor {
152 return (CGColorRef) self;
156 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
161 @interface UITextView (iPhoneOS)
162 - (void) setTextSize:(float)size;
165 @implementation UITextView (iPhoneOS)
167 - (void) setTextSize:(float)size {
168 [self setFont:[[self font] fontWithSize:size]];
175 OBJC_EXPORT const char *class_getName(Class cls);
177 /* Reset View (UIView) {{{ */
178 @interface UIView (RVBook)
179 - (void) resetViewAnimated:(BOOL)animated;
183 @implementation UIView (RVBook)
185 - (void) resetViewAnimated:(BOOL)animated {
186 fprintf(stderr, "%s\n", class_getName(self->isa));
191 fprintf(stderr, "%s\n", class_getName(self->isa));
197 /* Reset View (UITable) {{{ */
198 @interface UITable (RVBook)
199 - (void) resetViewAnimated:(BOOL)animated;
203 @implementation UITable (RVBook)
205 - (void) resetViewAnimated:(BOOL)animated {
206 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
215 /* Reset View (UISectionList) {{{ */
216 @interface UISectionList (RVBook)
217 - (void) resetViewAnimated:(BOOL)animated;
221 @implementation UISectionList (RVBook)
223 - (void) resetViewAnimated:(BOOL)animated {
224 [[self table] resetViewAnimated:animated];
228 [[self table] clearView];
234 /* Perl-Compatible RegEx {{{ */
244 Pcre(const char *regex) :
249 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
252 fprintf(stderr, "%d:%s\n", offset, error);
256 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
257 matches_ = new int[(capture_ + 1) * 3];
265 NSString *operator [](size_t match) {
267 stringWithCString:(data_ + matches_[match * 2])
268 length:(matches_[match * 2 + 1] - matches_[match * 2])
272 bool operator ()(const char *data, size_t size) {
274 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
278 /* Mime Addresses {{{ */
279 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
281 @interface Address : NSObject {
287 - (NSString *) email;
289 + (Address *) addressWithString:(NSString *)string;
290 - (Address *) initWithString:(NSString *)string;
293 @implementation Address
302 - (NSString *) name {
306 - (NSString *) email {
310 + (Address *) addressWithString:(NSString *)string {
311 return [[[Address alloc] initWithString:string] autorelease];
314 - (Address *) initWithString:(NSString *)string {
315 if ((self = [super init]) != nil) {
316 const char *data = [string UTF8String];
317 size_t size = [string length];
319 if (email_r(data, size)) {
320 name_ = [email_r[1] retain];
321 email_ = [email_r[2] retain];
323 name_ = [[NSString stringWithCString:data length:size] retain];
331 /* CoreGraphics Primitives {{{ */
342 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
345 Set(space, red, green, blue, alpha);
350 CGColorRelease(color_);
357 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
359 float color[] = {red, green, blue, alpha};
360 color_ = CGColorCreate(space, color);
363 operator CGColorRef() {
378 /* Right Alignment {{{ */
379 @interface UIRightTextLabel : UITextLabel {
380 float _savedRightEdgeX;
381 BOOL _sizedtofit_flag;
384 - (void) setFrame:(CGRect)frame;
385 - (void) setText:(NSString *)text;
386 - (void) realignText;
389 @implementation UIRightTextLabel
391 - (void) setFrame:(CGRect)frame {
392 [super setFrame:frame];
393 if (_sizedtofit_flag == NO) {
394 _savedRightEdgeX = frame.origin.x;
399 - (void) setText:(NSString *)text {
400 [super setText:text];
404 - (void) realignText {
405 CGRect oldFrame = [self frame];
407 _sizedtofit_flag = YES;
408 [self sizeToFit]; // shrink down size so I can right align it
410 CGRect newFrame = [self frame];
412 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
413 oldFrame.size.width = newFrame.size.width;
414 [super setFrame:oldFrame];
415 _sizedtofit_flag = NO;
421 /* Random Global Variables {{{ */
422 static const int PulseInterval_ = 50000;
424 static CGColor Black_;
425 static CGColor Clear_;
427 static CGColor White_;
429 static NSString *Home_;
430 static BOOL Sounds_Keyboard_;
432 const char *Firmware_ = NULL;
433 const char *Machine_ = NULL;
434 const char *SerialNumber_ = NULL;
440 CGColorSpaceRef space_;
442 #define FW_LEAST(major, minor, bugfix) \
443 (major < Major_ || major == Major_ && \
444 (minor < Minor_ || minor == Minor_ && \
450 static NSMutableDictionary *Metadata_;
451 static NSMutableDictionary *Packages_;
454 NSString *GetLastUpdate() {
455 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
458 return @"Never or Unknown";
460 CFLocaleRef locale = CFLocaleCopyCurrent();
461 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
462 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
464 CFRelease(formatter);
467 return [(NSString *) formatted autorelease];
470 /* Display Helpers {{{ */
471 inline float Interpolate(float begin, float end, float fraction) {
472 return (end - begin) * fraction + begin;
475 NSString *SizeString(double size) {
477 while (size > 1024) {
482 static const char *powers_[] = {"B", "kB", "MB", "GB"};
484 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
487 static const float TextViewOffset_ = 22;
489 UITextView *GetTextView(NSString *value, float left, bool html) {
490 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
491 [text setEditable:NO];
492 [text setTextSize:16];
494 [text setHTML:value];
496 [text setText:value];
497 [text setEnabled:NO];
499 [text setBackgroundColor:Clear_];
501 CGRect frame = [text frame];
502 [text setFrame:frame];
503 CGRect rect = [text visibleTextRect];
504 frame.size.height = rect.size.height;
505 [text setFrame:frame];
510 NSString *Simplify(NSString *title) {
511 const char *data = [title UTF8String];
512 size_t size = [title length];
514 Pcre title_r("^(.*?)( \\(.*\\))?$");
515 if (title_r(data, size))
525 @protocol ProgressDelegate
526 - (void) setProgressError:(NSString *)error;
527 - (void) setProgressTitle:(NSString *)title;
528 - (void) setProgressPercent:(float)percent;
529 - (void) addProgressOutput:(NSString *)output;
532 @protocol CydiaDelegate
533 - (void) installPackage:(Package *)package;
534 - (void) removePackage:(Package *)package;
535 - (void) slideUp:(UIAlertSheet *)alert;
536 - (void) distUpgrade;
539 /* Status Delegation {{{ */
541 public pkgAcquireStatus
544 _transient id<ProgressDelegate> delegate_;
552 void setDelegate(id delegate) {
553 delegate_ = delegate;
556 virtual bool MediaChange(std::string media, std::string drive) {
560 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
563 virtual void Fetch(pkgAcquire::ItemDesc &item) {
564 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
567 virtual void Done(pkgAcquire::ItemDesc &item) {
570 virtual void Fail(pkgAcquire::ItemDesc &item) {
572 item.Owner->Status == pkgAcquire::Item::StatIdle ||
573 item.Owner->Status == pkgAcquire::Item::StatDone
577 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
580 virtual bool Pulse(pkgAcquire *Owner) {
581 bool value = pkgAcquireStatus::Pulse(Owner);
584 double(CurrentBytes + CurrentItems) /
585 double(TotalBytes + TotalItems)
588 [delegate_ setProgressPercent:percent];
592 virtual void Start() {
595 virtual void Stop() {
599 /* Progress Delegation {{{ */
604 _transient id<ProgressDelegate> delegate_;
607 virtual void Update() {
608 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
609 [delegate_ setProgressPercent:(Percent / 100)];
618 void setDelegate(id delegate) {
619 delegate_ = delegate;
622 virtual void Done() {
623 [delegate_ setProgressPercent:1];
628 /* Database Interface {{{ */
629 @interface Database : NSObject {
631 pkgRecords *records_;
632 pkgProblemResolver *resolver_;
633 pkgAcquire *fetcher_;
635 SPtr<pkgPackageManager> manager_;
636 pkgSourceList *list_;
638 NSMutableDictionary *sources_;
639 NSMutableArray *packages_;
641 _transient id delegate_;
647 - (void) _readStatus:(NSNumber *)fd;
648 - (void) _readOutput:(NSNumber *)fd;
650 - (Package *) packageWithName:(NSString *)name;
653 - (pkgCacheFile &) cache;
654 - (pkgRecords *) records;
655 - (pkgProblemResolver *) resolver;
656 - (pkgAcquire &) fetcher;
657 - (NSArray *) packages;
665 - (void) updateWithStatus:(Status &)status;
667 - (void) setDelegate:(id)delegate;
668 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
672 /* Source Class {{{ */
673 @interface Source : NSObject {
674 NSString *description_;
679 NSString *distribution_;
683 NSString *defaultIcon_;
688 - (Source *) initWithMetaIndex:(metaIndex *)index;
693 - (NSString *) distribution;
696 - (NSString *) description;
697 - (NSString *) label;
698 - (NSString *) origin;
699 - (NSString *) version;
701 - (NSString *) defaultIcon;
704 @implementation Source
708 [distribution_ release];
711 if (description_ != nil)
712 [description_ release];
719 if (defaultIcon_ != nil)
720 [defaultIcon_ release];
725 - (Source *) initWithMetaIndex:(metaIndex *)index {
726 if ((self = [super init]) != nil) {
727 trusted_ = index->IsTrusted();
729 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
730 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
731 type_ = [[NSString stringWithCString:index->GetType()] retain];
739 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
740 if (dindex != NULL) {
741 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
743 while (std::getline(release, line)) {
744 std::string::size_type colon(line.find(':'));
745 if (colon == std::string::npos)
748 std::string name(line.substr(0, colon));
749 std::string value(line.substr(colon + 1));
750 while (!value.empty() && value[0] == ' ')
751 value = value.substr(1);
753 if (name == "Default-Icon")
754 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
755 else if (name == "Description")
756 description_ = [[NSString stringWithCString:value.c_str()] retain];
757 else if (name == "Label")
758 label_ = [[NSString stringWithCString:value.c_str()] retain];
759 else if (name == "Origin")
760 origin_ = [[NSString stringWithCString:value.c_str()] retain];
761 else if (name == "Version")
762 version_ = [[NSString stringWithCString:value.c_str()] retain];
776 - (NSString *) distribution {
777 return distribution_;
780 - (NSString *) type {
784 - (NSString *) description {
788 - (NSString *) label {
792 - (NSString *) origin {
796 - (NSString *) version {
800 - (NSString *) defaultIcon {
806 /* Package Class {{{ */
807 NSString *Scour(const char *field, const char *begin, const char *end) {
808 size_t i(0), l(strlen(field));
811 const char *name = begin + i;
812 const char *colon = name + l;
813 const char *value = colon + 1;
818 memcmp(name, field, l) == 0
820 while (value != end && value[0] == ' ')
822 const char *line = std::find(value, end, '\n');
823 while (line != value && line[-1] == ' ')
825 return [NSString stringWithCString:value length:(line - value)];
827 begin = std::find(begin, end, '\n');
835 @interface Package : NSObject {
836 pkgCache::PkgIterator iterator_;
837 _transient Database *database_;
838 pkgCache::VerIterator version_;
839 pkgCache::VerFileIterator file_;
843 NSString *installed_;
852 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
853 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
855 - (NSString *) section;
856 - (Address *) maintainer;
858 - (NSString *) description;
859 - (NSString *) index;
863 - (NSString *) latest;
864 - (NSString *) installed;
871 - (NSString *) tagline;
873 - (NSString *) website;
877 - (BOOL) matches:(NSString *)text;
879 - (NSComparisonResult) compareByName:(Package *)package;
880 - (NSComparisonResult) compareBySection:(Package *)package;
881 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
882 - (NSComparisonResult) compareForChanges:(Package *)package;
887 - (NSNumber *) isSearchedForBy:(NSString *)search;
888 - (NSNumber *) isInstalledInSection:(NSString *)section;
889 - (NSNumber *) isUninstalledInSection:(NSString *)section;
893 @implementation Package
897 if (installed_ != nil)
898 [installed_ release];
914 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
915 if ((self = [super init]) != nil) {
916 iterator_ = iterator;
917 database_ = database;
922 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
924 const char *begin, *end;
925 parser->GetRec(begin, end);
927 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
928 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
930 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
931 name_ = Scour("Name", begin, end);
933 name_ = [name_ retain];
934 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
935 icon_ = Scour("Icon", begin, end);
937 icon_ = [icon_ retain];
938 website_ = Scour("Website", begin, end);
940 website_ = [website_ retain];
942 source_ = [[database_ getSource:file_.File()] retain];
944 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
945 if (metadata == nil || [metadata count] == 0) {
946 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
950 [Packages_ setObject:metadata forKey:id_];
955 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
956 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
957 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
958 return [[[Package alloc]
959 initWithIterator:iterator
967 - (NSString *) section {
968 const char *section = iterator_.Section();
969 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
972 - (Address *) maintainer {
973 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
974 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
978 return version_->InstalledSize;
981 - (NSString *) description {
982 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
983 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
985 NSArray *lines = [description componentsSeparatedByString:@"\n"];
986 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
987 if ([lines count] < 2)
990 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
991 for (size_t i(1); i != [lines count]; ++i) {
992 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
993 [trimmed addObject:trim];
996 return [trimmed componentsJoinedByString:@"\n"];
999 - (NSString *) index {
1000 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1001 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1005 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1008 - (NSString *) latest {
1012 - (NSString *) installed {
1016 - (BOOL) upgradable {
1017 if (NSString *installed = [self installed])
1018 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1020 return [self essential];
1023 - (BOOL) essential {
1024 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1028 return (*[database_ cache])[iterator_].InstBroken();
1035 - (NSString *) name {
1036 return name_ == nil ? id_ : name_;
1039 - (NSString *) tagline {
1043 - (NSString *) icon {
1047 - (NSString *) website {
1051 - (Source *) source {
1055 - (BOOL) matches:(NSString *)text {
1061 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1062 if (range.location != NSNotFound)
1065 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1066 if (range.location != NSNotFound)
1069 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1070 if (range.location != NSNotFound)
1076 - (NSComparisonResult) compareByName:(Package *)package {
1077 NSString *lhs = [self name];
1078 NSString *rhs = [package name];
1080 if ([lhs length] != 0 && [rhs length] != 0) {
1081 unichar lhc = [lhs characterAtIndex:0];
1082 unichar rhc = [rhs characterAtIndex:0];
1084 if (isalpha(lhc) && !isalpha(rhc))
1085 return NSOrderedAscending;
1086 else if (!isalpha(lhc) && isalpha(rhc))
1087 return NSOrderedDescending;
1090 return [lhs caseInsensitiveCompare:rhs];
1093 - (NSComparisonResult) compareBySection:(Package *)package {
1094 NSString *lhs = [self section];
1095 NSString *rhs = [package section];
1097 if (lhs == NULL && rhs != NULL)
1098 return NSOrderedAscending;
1099 else if (lhs != NULL && rhs == NULL)
1100 return NSOrderedDescending;
1101 else if (lhs != NULL && rhs != NULL) {
1102 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1103 if (result != NSOrderedSame)
1107 return NSOrderedSame;
1110 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1111 NSString *lhs = [self section];
1112 NSString *rhs = [package section];
1114 if (lhs == NULL && rhs != NULL)
1115 return NSOrderedAscending;
1116 else if (lhs != NULL && rhs == NULL)
1117 return NSOrderedDescending;
1118 else if (lhs != NULL && rhs != NULL) {
1119 NSComparisonResult result = [lhs compare:rhs];
1120 if (result != NSOrderedSame)
1124 return [self compareByName:package];
1127 - (NSComparisonResult) compareForChanges:(Package *)package {
1128 BOOL lhs = [self upgradable];
1129 BOOL rhs = [package upgradable];
1132 return lhs ? NSOrderedAscending : NSOrderedDescending;
1134 switch ([[self seen] compare:[package seen]]) {
1135 case NSOrderedAscending:
1136 return NSOrderedDescending;
1139 case NSOrderedDescending:
1140 return NSOrderedAscending;
1146 return [self compareByName:package];
1150 pkgProblemResolver *resolver = [database_ resolver];
1151 resolver->Clear(iterator_);
1152 resolver->Protect(iterator_);
1153 pkgCacheFile &cache([database_ cache]);
1154 cache->MarkInstall(iterator_, false);
1155 pkgDepCache::StateCache &state((*cache)[iterator_]);
1156 if (!state.Install())
1157 cache->SetReInstall(iterator_, true);
1161 pkgProblemResolver *resolver = [database_ resolver];
1162 resolver->Clear(iterator_);
1163 resolver->Protect(iterator_);
1164 resolver->Remove(iterator_);
1165 [database_ cache]->MarkDelete(iterator_, true);
1168 - (NSNumber *) isSearchedForBy:(NSString *)search {
1169 return [NSNumber numberWithBool:[self matches:search]];
1172 - (NSNumber *) isInstalledInSection:(NSString *)section {
1173 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1176 - (NSNumber *) isUninstalledInSection:(NSString *)section {
1177 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1182 /* Section Class {{{ */
1183 @interface Section : NSObject {
1189 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1190 - (NSString *) name;
1193 - (void) addToCount;
1197 @implementation Section
1204 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1205 if ((self = [super init]) != nil) {
1206 name_ = [name retain];
1211 - (NSString *) name {
1223 - (void) addToCount {
1230 /* Database Implementation {{{ */
1231 @implementation Database
1238 - (void) _readStatus:(NSNumber *)fd {
1239 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1241 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1242 std::istream is(&ib);
1247 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1249 pcre_extra *study = NULL;
1251 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1252 int matches[(capture + 1) * 3];
1254 while (std::getline(is, line)) {
1255 const char *data(line.c_str());
1257 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1259 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1262 [delegate_ setProgressPercent:(percent / 100)];
1264 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1265 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1267 if (type == "pmerror")
1268 [delegate_ setProgressError:string];
1269 else if (type == "pmstatus")
1270 [delegate_ setProgressTitle:string];
1271 else if (type == "pmconffile")
1273 else _assert(false);
1280 - (void) _readOutput:(NSNumber *)fd {
1281 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1283 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1284 std::istream is(&ib);
1287 while (std::getline(is, line))
1288 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1294 - (Package *) packageWithName:(NSString *)name {
1295 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1296 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1299 - (Database *) init {
1300 if ((self = [super init]) != nil) {
1306 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1307 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1311 _assert(pipe(fds) != -1);
1315 detachNewThreadSelector:@selector(_readStatus:)
1317 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1320 _assert(pipe(fds) != -1);
1321 _assert(dup2(fds[1], 1) != -1);
1322 _assert(close(fds[1]) != -1);
1325 detachNewThreadSelector:@selector(_readOutput:)
1327 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1332 - (pkgCacheFile &) cache {
1336 - (pkgRecords *) records {
1340 - (pkgProblemResolver *) resolver {
1344 - (pkgAcquire &) fetcher {
1348 - (NSArray *) packages {
1352 - (void) reloadData {
1362 if (!cache_.Open(progress_, true)) {
1363 fprintf(stderr, "repairing corrupted database...\n");
1365 [self updateWithStatus:status_];
1366 _assert(cache_.Open(progress_, true));
1369 now_ = [[NSDate date] retain];
1371 records_ = new pkgRecords(cache_);
1372 resolver_ = new pkgProblemResolver(cache_);
1373 fetcher_ = new pkgAcquire(&status_);
1376 list_ = new pkgSourceList();
1377 _assert(list_->ReadMainList());
1379 [sources_ removeAllObjects];
1380 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1381 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1382 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1384 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1385 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1389 [packages_ removeAllObjects];
1390 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1391 if (Package *package = [Package packageWithIterator:iterator database:self])
1392 if ([package source] != nil || [package installed] != nil)
1393 [packages_ addObject:package];
1397 pkgRecords records(cache_);
1399 lock_ = new FileFd();
1400 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1401 _assert(!_error->PendingError());
1404 // XXX: explain this with an error message
1405 _assert(list.ReadMainList());
1407 manager_ = (_system->CreatePM(cache_));
1408 _assert(manager_->GetArchives(fetcher_, &list, &records));
1409 _assert(!_error->PendingError());
1413 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1415 _assert(list.ReadMainList());
1416 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1417 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1420 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1424 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1426 if (result == pkgPackageManager::Failed)
1428 if (_error->PendingError())
1430 if (result != pkgPackageManager::Completed)
1433 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1435 _assert(list.ReadMainList());
1436 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1437 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1440 if (![before isEqualToArray:after])
1445 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1446 _assert(pkgApplyStatus(cache_));
1448 if (cache_->BrokenCount() != 0) {
1449 _assert(pkgFixBroken(cache_));
1450 _assert(cache_->BrokenCount() == 0);
1451 _assert(pkgMinimizeUpgrade(cache_));
1454 _assert(pkgDistUpgrade(cache_));
1458 [self updateWithStatus:status_];
1461 - (void) updateWithStatus:(Status &)status {
1463 _assert(list.ReadMainList());
1466 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1467 _assert(!_error->PendingError());
1469 pkgAcquire fetcher(&status);
1470 _assert(list.GetIndexes(&fetcher));
1472 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1473 bool failed = false;
1474 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1475 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1476 (*item)->Finished();
1480 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1481 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1482 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1485 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1489 - (void) setDelegate:(id)delegate {
1490 delegate_ = delegate;
1491 status_.setDelegate(delegate);
1492 progress_.setDelegate(delegate);
1495 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1496 pkgIndexFile *index(NULL);
1497 list_->FindIndex(file, index);
1498 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1504 /* RVPage Interface {{{ */
1507 @interface RVPage : UIView {
1508 _transient RVBook *book_;
1509 _transient id delegate_;
1512 - (NSString *) title;
1513 - (NSString *) backButtonTitle;
1514 - (NSString *) rightButtonTitle;
1515 - (NSString *) leftButtonTitle;
1516 - (UIView *) accessoryView;
1518 - (void) _rightButtonClicked;
1519 - (void) _leftButtonClicked;
1521 - (void) setPageActive:(BOOL)active;
1522 - (void) resetViewAnimated:(BOOL)animated;
1524 - (void) setTitle:(NSString *)title;
1525 - (void) setBackButtonTitle:(NSString *)title;
1527 - (void) reloadButtons;
1528 - (void) reloadData;
1530 - (id) initWithBook:(RVBook *)book;
1532 - (void) setDelegate:(id)delegate;
1536 /* Reset View {{{ */
1537 @protocol RVDelegate
1538 - (void) setPageActive:(BOOL)active with:(id)object;
1539 - (void) resetViewAnimated:(BOOL)animated with:(id)object;
1540 - (void) reloadDataWith:(id)object;
1543 @interface RVBook : UIView {
1544 NSMutableArray *pages_;
1545 UINavigationBar *navbar_;
1546 UITransitionView *transition_;
1548 _transient id delegate_;
1551 - (id) initWithFrame:(CGRect)frame;
1552 - (void) setDelegate:(id)delegate;
1554 - (void) setPage:(RVPage *)page;
1556 - (void) pushPage:(RVPage *)page;
1557 - (void) popPages:(unsigned)pages;
1559 - (void) setPrompt:(NSString *)prompt;
1561 - (void) resetViewAnimated:(BOOL)animated;
1562 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
1564 - (void) setTitle:(NSString *)title forPage:(RVPage *)page;
1565 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
1566 - (void) reloadButtonsForPage:(RVPage *)page;
1568 - (void) reloadData;
1570 - (CGRect) pageBounds;
1574 @implementation RVBook
1577 [navbar_ setDelegate:nil];
1581 [transition_ release];
1585 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1586 _assert([pages_ count] != 0);
1587 RVPage *page = [pages_ lastObject];
1589 case 0: [page _rightButtonClicked]; break;
1590 case 1: [page _leftButtonClicked]; break;
1594 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
1595 _assert([pages_ count] != 0);
1597 [[pages_ lastObject] setPageActive:NO];
1598 [pages_ removeLastObject];
1600 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1603 - (id) initWithFrame:(CGRect)frame {
1604 if ((self = [super initWithFrame:frame]) != nil) {
1605 pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
1607 struct CGRect bounds = [self bounds];
1608 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
1609 CGRect navrect = {{0, 0}, navsize};
1611 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1612 [self addSubview:navbar_];
1614 [navbar_ setBarStyle:1];
1615 [navbar_ setDelegate:self];
1617 [navbar_ setPrompt:@""];
1619 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
1620 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
1623 [self addSubview:transition_];
1627 - (void) setDelegate:(id)delegate {
1628 delegate_ = delegate;
1631 - (void) setPage:(RVPage *)page {
1632 if ([pages_ count] != 0)
1633 [[pages_ lastObject] setPageActive:NO];
1635 [navbar_ disableAnimation];
1637 for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
1638 [navbar_ popNavigationItem];
1641 [self pushPage:page];
1642 [navbar_ enableAnimation];
1645 - (void) pushPage:(RVPage *)page {
1646 if ([pages_ count] != 0)
1647 [[pages_ lastObject] setPageActive:NO];
1649 NSString *title = Simplify([page title]);
1651 NSString *backButtonTitle = [page backButtonTitle];
1652 if (backButtonTitle == nil)
1653 backButtonTitle = title;
1655 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1656 [navitem setBackButtonTitle:backButtonTitle];
1657 [navbar_ pushNavigationItem:navitem];
1659 BOOL animated = [pages_ count] == 0 ? NO : YES;
1660 [transition_ transition:(animated ? 1 : 0) toView:page];
1661 [page setPageActive:YES];
1663 [pages_ addObject:page];
1664 [self reloadButtonsForPage:page];
1666 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
1669 - (void) popPages:(unsigned)pages {
1673 [[pages_ lastObject] setPageActive:NO];
1676 for (unsigned i(0); i != pages; ++i)
1677 [navbar_ popNavigationItem];
1680 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1683 - (void) setPrompt:(NSString *)prompt {
1684 [navbar_ setPrompt:prompt];
1687 - (void) resetViewAnimated:(BOOL)animated {
1690 if ([pages_ count] > 1) {
1691 [navbar_ disableAnimation];
1692 while ([pages_ count] != (animated ? 2 : 1))
1693 [navbar_ popNavigationItem];
1694 [navbar_ enableAnimation];
1696 [navbar_ popNavigationItem];
1701 [self resetViewAnimated:animated toPage:[pages_ lastObject]];
1704 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
1705 [page resetViewAnimated:animated];
1706 [transition_ transition:(animated ? 2 : 0) toView:page];
1707 [page setPageActive:YES];
1708 [self reloadButtonsForPage:page];
1709 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
1712 - (void) setTitle:(NSString *)title forPage:(RVPage *)page {
1713 if ([pages_ count] == 0 || page != [pages_ lastObject])
1715 UINavigationItem *navitem = [navbar_ topItem];
1716 [navitem setTitle:title];
1719 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
1720 if ([pages_ count] == 0 || page != [pages_ lastObject])
1722 UINavigationItem *navitem = [navbar_ topItem];
1723 [navitem setBackButtonTitle:title];
1726 - (void) reloadButtonsForPage:(RVPage *)page {
1727 if ([pages_ count] == 0 || page != [pages_ lastObject])
1729 NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
1730 [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
1733 - (void) reloadData {
1734 for (int i(0), e([pages_ count]); i != e; ++i) {
1735 RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
1740 - (CGRect) pageBounds {
1741 return [transition_ bounds];
1746 /* RVPage Implementation {{{ */
1747 @implementation RVPage
1749 - (NSString *) title {
1750 [self doesNotRecognizeSelector:_cmd];
1754 - (NSString *) backButtonTitle {
1758 - (NSString *) leftButtonTitle {
1762 - (NSString *) rightButtonTitle {
1766 - (void) _rightButtonClicked {
1767 [self doesNotRecognizeSelector:_cmd];
1770 - (void) _leftButtonClicked {
1771 [self doesNotRecognizeSelector:_cmd];
1774 - (UIView *) accessoryView {
1778 - (void) setPageActive:(BOOL)active {
1781 - (void) resetViewAnimated:(BOOL)animated {
1782 [self doesNotRecognizeSelector:_cmd];
1785 - (void) setTitle:(NSString *)title {
1786 [book_ setTitle:title forPage:self];
1789 - (void) setBackButtonTitle:(NSString *)title {
1790 [book_ setBackButtonTitle:title forPage:self];
1793 - (void) reloadButtons {
1794 [book_ reloadButtonsForPage:self];
1797 - (void) reloadData {
1800 - (id) initWithBook:(RVBook *)book {
1801 if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
1806 - (void) setDelegate:(id)delegate {
1807 delegate_ = delegate;
1813 /* Confirmation View {{{ */
1814 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1815 if ([packages count] == 0)
1818 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1819 [fields setObject:text forKey:key];
1821 CGColor blue(space_, 0, 0, 0.4, 1);
1822 [text setTextColor:blue];
1825 @protocol ConfirmationViewDelegate
1830 @interface ConfirmationView : UIView {
1831 Database *database_;
1833 UITransitionView *transition_;
1835 UINavigationBar *navbar_;
1836 UIPreferencesTable *table_;
1837 NSMutableDictionary *fields_;
1838 UIAlertSheet *essential_;
1843 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1847 @implementation ConfirmationView
1850 [navbar_ setDelegate:nil];
1851 [transition_ setDelegate:nil];
1852 [table_ setDataSource:nil];
1854 [transition_ release];
1859 if (essential_ != nil)
1860 [essential_ release];
1865 [transition_ transition:7 toView:nil];
1869 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1870 if (from != nil && to == nil)
1871 [self removeFromSuperview];
1874 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1877 if (essential_ != nil)
1878 [essential_ popupAlertAnimated:YES];
1880 [delegate_ confirm];
1889 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1890 [essential_ dismiss];
1894 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1898 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1900 case 0: return @"Statistics";
1901 case 1: return @"Modifications";
1903 default: _assert(false);
1907 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1910 case 1: return [fields_ count];
1912 default: _assert(false);
1916 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1917 if (group != 1 || row == -1)
1920 _assert(size_t(row) < [fields_ count]);
1921 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1925 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1926 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1927 [cell setShowSelection:NO];
1930 case 0: switch (row) {
1932 [cell setTitle:@"Downloading"];
1933 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1937 [cell setTitle:@"Resuming At"];
1938 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1942 double size([database_ cache]->UsrSize());
1945 [cell setTitle:@"Disk Freeing"];
1946 [cell setValue:SizeString(-size)];
1948 [cell setTitle:@"Disk Using"];
1949 [cell setValue:SizeString(size)];
1953 default: _assert(false);
1957 _assert(size_t(row) < [fields_ count]);
1958 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1959 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1962 default: _assert(false);
1968 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1969 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1970 database_ = database;
1971 delegate_ = delegate;
1973 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1974 [self addSubview:transition_];
1976 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1978 CGSize navsize = [UINavigationBar defaultSize];
1979 CGRect navrect = {{0, 0}, navsize};
1980 CGRect bounds = [overlay_ bounds];
1982 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1983 [navbar_ setBarStyle:1];
1984 [navbar_ setDelegate:self];
1986 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1987 [navbar_ pushNavigationItem:navitem];
1988 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1990 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1992 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1993 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1994 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1995 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1996 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2000 pkgCacheFile &cache([database_ cache]);
2001 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
2002 Package *package([Package packageWithIterator:iterator database:database_]);
2003 NSString *name([package name]);
2004 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
2005 pkgDepCache::StateCache &state(cache[iterator]);
2007 if (state.NewInstall())
2008 [installing addObject:name];
2009 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2010 [reinstalling addObject:name];
2011 else if (state.Upgrade())
2012 [upgrading addObject:name];
2013 else if (state.Downgrade())
2014 [downgrading addObject:name];
2015 else if (state.Delete()) {
2018 [removing addObject:name];
2025 essential_ = [[UIAlertSheet alloc]
2026 initWithTitle:@"Unable to Comply"
2027 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2028 defaultButtonIndex:0
2033 [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."];
2036 AddTextView(fields_, installing, @"Installing");
2037 AddTextView(fields_, reinstalling, @"Reinstalling");
2038 AddTextView(fields_, upgrading, @"Upgrading");
2039 AddTextView(fields_, downgrading, @"Downgrading");
2040 AddTextView(fields_, removing, @"Removing");
2042 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2043 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2046 [table_ setReusesTableCells:YES];
2047 [table_ setDataSource:self];
2048 [table_ reloadData];
2050 [overlay_ addSubview:navbar_];
2051 [overlay_ addSubview:table_];
2053 [view addSubview:self];
2055 [transition_ setDelegate:self];
2057 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2058 [transition_ transition:0 toView:blank];
2059 [transition_ transition:3 toView:overlay_];
2066 /* Progress Data {{{ */
2067 @interface ProgressData : NSObject {
2073 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2080 @implementation ProgressData
2082 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2083 if ((self = [super init]) != nil) {
2084 selector_ = selector;
2104 /* Progress View {{{ */
2105 @interface ProgressView : UIView <
2109 UIView *background_;
2110 UITransitionView *transition_;
2112 UINavigationBar *navbar_;
2113 UIProgressBar *progress_;
2114 UITextView *output_;
2115 UITextLabel *status_;
2119 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2121 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
2122 - (void) setContentView:(UIView *)view;
2125 - (void) _retachThread;
2126 - (void) _detachNewThreadData:(ProgressData *)data;
2127 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2131 @protocol ProgressViewDelegate
2132 - (void) progressViewIsComplete:(ProgressView *)sender;
2135 @implementation ProgressView
2138 [transition_ setDelegate:nil];
2139 [navbar_ setDelegate:nil];
2142 if (background_ != nil)
2143 [background_ release];
2144 [transition_ release];
2147 [progress_ release];
2153 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2154 if (bootstrap_ && from == overlay_ && to == view_)
2158 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
2159 if ((self = [super initWithFrame:frame]) != nil) {
2160 delegate_ = delegate;
2162 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2163 [transition_ setDelegate:self];
2165 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2168 [overlay_ setBackgroundColor:Black_];
2170 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2171 [background_ setBackgroundColor:Black_];
2172 [self addSubview:background_];
2175 [self addSubview:transition_];
2177 CGSize navsize = [UINavigationBar defaultSize];
2178 CGRect navrect = {{0, 0}, navsize};
2180 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2181 [overlay_ addSubview:navbar_];
2183 [navbar_ setBarStyle:1];
2184 [navbar_ setDelegate:self];
2186 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2187 [navbar_ pushNavigationItem:navitem];
2189 CGRect bounds = [overlay_ bounds];
2190 CGSize prgsize = [UIProgressBar defaultSize];
2193 (bounds.size.width - prgsize.width) / 2,
2194 bounds.size.height - prgsize.height - 20
2197 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2198 [overlay_ addSubview:progress_];
2200 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2202 bounds.size.height - prgsize.height - 50,
2203 bounds.size.width - 20,
2207 [status_ setColor:White_];
2208 [status_ setBackgroundColor:Clear_];
2210 [status_ setCentersHorizontally:YES];
2211 //[status_ setFont:font];
2213 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2215 navrect.size.height + 20,
2216 bounds.size.width - 20,
2217 bounds.size.height - navsize.height - 62 - navrect.size.height
2220 //[output_ setTextFont:@"Courier New"];
2221 [output_ setTextSize:12];
2223 [output_ setTextColor:White_];
2224 [output_ setBackgroundColor:Clear_];
2226 [output_ setMarginTop:0];
2227 [output_ setAllowsRubberBanding:YES];
2229 [overlay_ addSubview:output_];
2230 [overlay_ addSubview:status_];
2232 [progress_ setStyle:0];
2236 - (void) setContentView:(UIView *)view {
2237 view_ = [view retain];
2240 - (void) resetView {
2241 [transition_ transition:6 toView:view_];
2244 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2248 - (void) _retachThread {
2249 [delegate_ progressViewIsComplete:self];
2253 - (void) _detachNewThreadData:(ProgressData *)data {
2254 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2256 [[data target] performSelector:[data selector] withObject:[data object]];
2259 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2264 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2265 [navbar_ popNavigationItem];
2266 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
2267 [navbar_ pushNavigationItem:navitem];
2269 [status_ setText:nil];
2270 [output_ setText:@""];
2271 [progress_ setProgress:0];
2273 [transition_ transition:6 toView:overlay_];
2276 detachNewThreadSelector:@selector(_detachNewThreadData:)
2278 withObject:[[ProgressData alloc]
2279 initWithSelector:selector
2286 - (void) setProgressError:(NSString *)error {
2288 performSelectorOnMainThread:@selector(_setProgressError:)
2294 - (void) setProgressTitle:(NSString *)title {
2296 performSelectorOnMainThread:@selector(_setProgressTitle:)
2302 - (void) setProgressPercent:(float)percent {
2304 performSelectorOnMainThread:@selector(_setProgressPercent:)
2305 withObject:[NSNumber numberWithFloat:percent]
2310 - (void) addProgressOutput:(NSString *)output {
2312 performSelectorOnMainThread:@selector(_addProgressOutput:)
2318 - (void) _setProgressError:(NSString *)error {
2319 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2320 initWithTitle:@"Package Error"
2321 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2322 defaultButtonIndex:0
2327 [sheet setBodyText:error];
2328 [sheet popupAlertAnimated:YES];
2331 - (void) _setProgressTitle:(NSString *)title {
2332 [status_ setText:[title stringByAppendingString:@"..."]];
2335 - (void) _setProgressPercent:(NSNumber *)percent {
2336 [progress_ setProgress:[percent floatValue]];
2339 - (void) _addProgressOutput:(NSString *)output {
2340 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2341 CGSize size = [output_ contentSize];
2342 CGRect rect = {{0, size.height}, {size.width, 0}};
2343 [output_ scrollRectToVisible:rect animated:YES];
2349 /* Package Cell {{{ */
2350 @interface PackageCell : UITableCell {
2353 UITextLabel *description_;
2354 UITextLabel *source_;
2355 UIImageView *trusted_;
2358 - (PackageCell *) init;
2359 - (void) setPackage:(Package *)package;
2361 - (void) _setSelected:(float)fraction;
2362 - (void) setSelected:(BOOL)selected;
2363 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2364 - (void) _setSelectionFadeFraction:(float)fraction;
2368 @implementation PackageCell
2373 [description_ release];
2379 - (PackageCell *) init {
2380 if ((self = [super init]) != nil) {
2381 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2382 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2383 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2385 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2387 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2388 [name_ setBackgroundColor:Clear_];
2389 [name_ setFont:bold];
2391 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2392 [source_ setBackgroundColor:Clear_];
2393 [source_ setFont:large];
2395 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2396 [description_ setBackgroundColor:Clear_];
2397 [description_ setFont:small];
2399 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2400 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
2402 [self addSubview:icon_];
2403 [self addSubview:name_];
2404 [self addSubview:description_];
2405 [self addSubview:source_];
2413 - (void) setPackage:(Package *)package {
2414 Source *source = [package source];
2416 UIImage *image = nil;
2417 if (NSString *icon = [package icon])
2418 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2419 if (image == nil) if (NSString *icon = [source defaultIcon])
2420 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2422 image = [UIImage applicationImageNamed:@"unknown.png"];
2423 [icon_ setImage:image];
2426 CGSize size = [image size];
2427 float scale = 30 / std::max(size.width, size.height);
2428 [icon_ zoomToScale:scale];
2431 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2433 [name_ setText:[package name]];
2434 [description_ setText:[package tagline]];
2439 if (source != nil) {
2440 label = [source label];
2441 trusted = [source trusted];
2442 } else if ([[package id] isEqualToString:@"firmware"]) {
2446 label = @"Unknown/Local";
2450 [source_ setText:[NSString stringWithFormat:@"from %@ (%@)", label, Simplify([package section])]];
2453 [self addSubview:trusted_];
2455 [trusted_ removeFromSuperview];
2458 - (void) _setSelected:(float)fraction {
2459 CGColor black(space_,
2460 Interpolate(0.0, 1.0, fraction),
2461 Interpolate(0.0, 1.0, fraction),
2462 Interpolate(0.0, 1.0, fraction),
2465 CGColor gray(space_,
2466 Interpolate(0.4, 1.0, fraction),
2467 Interpolate(0.4, 1.0, fraction),
2468 Interpolate(0.4, 1.0, fraction),
2471 [name_ setColor:black];
2472 [description_ setColor:gray];
2473 [source_ setColor:black];
2476 - (void) setSelected:(BOOL)selected {
2477 [self _setSelected:(selected ? 1.0 : 0.0)];
2478 [super setSelected:selected];
2481 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2483 [self _setSelected:(selected ? 1.0 : 0.0)];
2484 [super setSelected:selected withFade:fade];
2487 - (void) _setSelectionFadeFraction:(float)fraction {
2488 [self _setSelected:fraction];
2489 [super _setSelectionFadeFraction:fraction];
2494 /* Section Cell {{{ */
2495 @interface SectionCell : UITableCell {
2497 UITextLabel *count_;
2501 - (void) setSection:(Section *)section;
2503 - (void) _setSelected:(float)fraction;
2504 - (void) setSelected:(BOOL)selected;
2505 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2506 - (void) _setSelectionFadeFraction:(float)fraction;
2510 @implementation SectionCell
2519 if ((self = [super init]) != nil) {
2520 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2521 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2523 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2524 [name_ setBackgroundColor:Clear_];
2525 [name_ setFont:bold];
2527 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2528 [count_ setCentersHorizontally:YES];
2529 [count_ setBackgroundColor:Clear_];
2530 [count_ setFont:small];
2531 [count_ setColor:White_];
2533 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2534 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2536 [self addSubview:folder];
2537 [self addSubview:name_];
2538 [self addSubview:count_];
2540 [self _setSelected:0];
2547 - (void) setSection:(Section *)section {
2548 if (section == nil) {
2549 [name_ setText:@"All Packages"];
2550 [count_ setText:nil];
2552 NSString *name = [section name];
2553 [name_ setText:(name == nil ? @"(No Section)" : name)];
2554 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2558 - (void) _setSelected:(float)fraction {
2559 CGColor black(space_,
2560 Interpolate(0.0, 1.0, fraction),
2561 Interpolate(0.0, 1.0, fraction),
2562 Interpolate(0.0, 1.0, fraction),
2565 [name_ setColor:black];
2568 - (void) setSelected:(BOOL)selected {
2569 [self _setSelected:(selected ? 1.0 : 0.0)];
2570 [super setSelected:selected];
2573 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2575 [self _setSelected:(selected ? 1.0 : 0.0)];
2576 [super setSelected:selected withFade:fade];
2579 - (void) _setSelectionFadeFraction:(float)fraction {
2580 [self _setSelected:fraction];
2581 [super _setSelectionFadeFraction:fraction];
2587 /* Browser Interface {{{ */
2588 @interface BrowserView : RVPage {
2589 _transient Database *database_;
2590 UIScroller *scroller_;
2591 UIWebView *webview_;
2592 NSMutableArray *urls_;
2593 UIProgressIndicator *indicator_;
2600 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2601 - (void) loadURL:(NSURL *)url;
2603 - (void) loadRequest:(NSURLRequest *)request;
2606 - (WebView *) webView;
2608 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2613 /* Package View {{{ */
2614 @protocol PackageViewDelegate
2615 - (void) performPackage:(Package *)package;
2618 @interface PackageView : RVPage {
2619 _transient Database *database_;
2620 UIPreferencesTable *table_;
2623 UITextView *description_;
2626 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2627 - (void) setPackage:(Package *)package;
2631 @implementation PackageView
2634 [table_ setDataSource:nil];
2635 [table_ setDelegate:nil];
2637 if (package_ != nil)
2641 if (description_ != nil)
2642 [description_ release];
2647 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2648 return [package_ source] == nil ? 2 : 3;
2651 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2654 case 1: return @"Package Details";
2655 case 2: return @"Source Information";
2657 default: _assert(false);
2661 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2662 if (group != 0 || row != 1)
2665 return [description_ visibleTextRect].size.height + TextViewOffset_;
2668 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2670 case 0: return [package_ website] == nil ? 2 : 3;
2674 default: _assert(false);
2678 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2679 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2680 [cell setShowSelection:NO];
2683 case 0: switch (row) {
2685 [cell setTitle:[package_ name]];
2686 [cell setValue:[package_ latest]];
2690 [cell addSubview:description_];
2694 [cell setTitle:@"More Information"];
2695 [cell setShowDisclosure:YES];
2696 [cell setShowSelection:YES];
2699 default: _assert(false);
2702 case 1: switch (row) {
2704 [cell setTitle:@"Identifier"];
2705 [cell setValue:[package_ id]];
2709 [cell setTitle:@"Installed Version"];
2710 NSString *installed([package_ installed]);
2711 [cell setValue:(installed == nil ? @"n/a" : installed)];
2715 [cell setTitle:@"Section"];
2716 NSString *section([package_ section]);
2717 [cell setValue:(section == nil ? @"n/a" : section)];
2721 [cell setTitle:@"Expanded Size"];
2722 [cell setValue:SizeString([package_ size])];
2726 [cell setTitle:@"Maintainer"];
2727 [cell setValue:[[package_ maintainer] name]];
2728 [cell setShowDisclosure:YES];
2729 [cell setShowSelection:YES];
2732 default: _assert(false);
2735 case 2: switch (row) {
2737 [cell setTitle:[[package_ source] label]];
2738 [cell setValue:[[package_ source] version]];
2742 [cell setValue:[[package_ source] description]];
2746 [cell setTitle:@"Origin"];
2747 [cell setValue:[[package_ source] origin]];
2750 default: _assert(false);
2753 default: _assert(false);
2759 - (BOOL) canSelectRow:(int)row {
2763 - (void) tableRowSelected:(NSNotification *)notification {
2764 int row = [table_ selectedRow];
2765 NSString *website = [package_ website];
2767 if (row == (website == nil ? 8 : 9))
2768 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2769 [[package_ maintainer] email],
2770 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2772 else if (website != nil && row == 3) {
2773 NSURL *url = [NSURL URLWithString:website];
2774 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2775 [browser setDelegate:delegate_];
2776 [book_ pushPage:browser];
2777 [browser loadURL:url];
2781 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2783 case 1: [delegate_ installPackage:package_]; break;
2784 case 2: [delegate_ removePackage:package_]; break;
2790 - (void) _rightButtonClicked {
2791 if ([package_ installed] == nil)
2792 [delegate_ installPackage:package_];
2794 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2796 if ([package_ upgradable])
2797 [buttons addObject:@"Upgrade"];
2799 [buttons addObject:@"Reinstall"];
2801 [buttons addObject:@"Remove"];
2802 [buttons addObject:@"Cancel"];
2804 [delegate_ slideUp:[[[UIAlertSheet alloc]
2805 initWithTitle:@"Manage Package"
2807 defaultButtonIndex:2
2814 - (NSString *) rightButtonTitle {
2815 _assert(package_ != nil);
2816 return [package_ installed] == nil ? @"Install" : @"Modify";
2819 - (NSString *) title {
2823 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2824 if ((self = [super initWithBook:book]) != nil) {
2825 database_ = database;
2827 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2828 [self addSubview:table_];
2830 [table_ setDataSource:self];
2831 [table_ setDelegate:self];
2835 - (void) setPackage:(Package *)package {
2836 if (package_ != nil) {
2837 [package_ autorelease];
2846 if (description_ != nil) {
2847 [description_ release];
2851 if (package != nil) {
2852 package_ = [package retain];
2853 name_ = [[package id] retain];
2855 NSString *description([package description]);
2856 if (description == nil)
2857 description = [package tagline];
2858 description_ = [GetTextView(description, 12, true) retain];
2860 [description_ setTextColor:Black_];
2862 [table_ reloadData];
2866 - (void) resetViewAnimated:(BOOL)animated {
2867 [table_ resetViewAnimated:animated];
2870 - (void) reloadData {
2871 [self setPackage:[database_ packageWithName:name_]];
2872 [self reloadButtons];
2877 /* Package Table {{{ */
2878 @interface PackageTable : RVPage {
2879 _transient Database *database_;
2883 NSMutableArray *packages_;
2884 NSMutableArray *sections_;
2885 UISectionList *list_;
2888 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2890 - (void) setDelegate:(id)delegate;
2891 - (void) setObject:(id)object;
2893 - (void) reloadData;
2894 - (void) resetCursor;
2898 @implementation PackageTable
2901 [list_ setDataSource:nil];
2906 [packages_ release];
2907 [sections_ release];
2912 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2913 return [sections_ count];
2916 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2917 return [[sections_ objectAtIndex:section] name];
2920 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2921 return [[sections_ objectAtIndex:section] row];
2924 - (int) numberOfRowsInTable:(UITable *)table {
2925 return [packages_ count];
2928 - (float) table:(UITable *)table heightForRow:(int)row {
2932 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2934 reusing = [[[PackageCell alloc] init] autorelease];
2935 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2939 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2943 - (void) tableRowSelected:(NSNotification *)notification {
2944 int row = [[notification object] selectedRow];
2948 Package *package = [packages_ objectAtIndex:row];
2949 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2950 [view setDelegate:delegate_];
2951 [view setPackage:package];
2952 [book_ pushPage:view];
2955 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2956 if ((self = [super initWithBook:book]) != nil) {
2957 database_ = database;
2958 title_ = [title retain];
2960 object_ = object == nil ? nil : [object retain];
2962 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2963 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2965 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2966 [list_ setDataSource:self];
2968 UITableColumn *column = [[[UITableColumn alloc]
2969 initWithTitle:@"Name"
2971 width:[self frame].size.width
2974 UITable *table = [list_ table];
2975 [table setSeparatorStyle:1];
2976 [table addTableColumn:column];
2977 [table setDelegate:self];
2978 [table setReusesTableCells:YES];
2980 [self addSubview:list_];
2985 - (void) setDelegate:(id)delegate {
2986 delegate_ = delegate;
2989 - (void) setObject:(id)object {
2995 object_ = [object retain];
2998 - (void) reloadData {
2999 NSArray *packages = [database_ packages];
3001 [packages_ removeAllObjects];
3002 [sections_ removeAllObjects];
3004 for (size_t i(0); i != [packages count]; ++i) {
3005 Package *package([packages objectAtIndex:i]);
3006 if ([[package performSelector:filter_ withObject:object_] boolValue])
3007 [packages_ addObject:package];
3010 [packages_ sortUsingSelector:@selector(compareByName:)];
3012 Section *section = nil;
3014 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3015 Package *package = [packages_ objectAtIndex:offset];
3016 NSString *name = [package index];
3018 if (section == nil || ![[section name] isEqualToString:name]) {
3019 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3020 [sections_ addObject:section];
3023 [section addToCount];
3029 - (NSString *) title {
3033 - (void) resetViewAnimated:(BOOL)animated {
3034 [list_ resetViewAnimated:animated];
3037 - (void) resetCursor {
3038 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3044 /* Browser Implementation {{{ */
3045 @implementation BrowserView
3048 WebView *webview = [webview_ webView];
3049 [webview setFrameLoadDelegate:nil];
3050 [webview setResourceLoadDelegate:nil];
3051 [webview setUIDelegate:nil];
3053 [scroller_ setDelegate:nil];
3054 [webview_ setDelegate:nil];
3056 [scroller_ release];
3059 [indicator_ release];
3065 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3066 NSMutableURLRequest *request = [NSMutableURLRequest
3069 timeoutInterval:30.0
3072 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3073 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3074 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3076 [self loadRequest:request];
3080 - (void) loadURL:(NSURL *)url {
3081 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3084 // XXX: this needs to add the headers
3085 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3089 - (void) loadRequest:(NSURLRequest *)request {
3090 [webview_ loadRequest:request];
3093 - (void) reloadURL {
3094 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3095 [urls_ removeLastObject];
3096 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3099 - (WebView *) webView {
3100 return [webview_ webView];
3103 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3104 [scroller_ setContentSize:frame.size];
3107 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3108 [self view:sender didSetFrame:frame];
3111 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3112 return [self _addHeadersToRequest:request];
3115 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3116 [self setBackButtonTitle:title_];
3117 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3118 [browser setDelegate:delegate_];
3119 [book_ pushPage:browser];
3120 [browser loadRequest:[self _addHeadersToRequest:request]];
3121 return [browser webView];
3124 - (void) webView:(WebView *)sender willClickElement:(id)element {
3125 if (![element respondsToSelector:@selector(href)])
3127 NSString *href = [element href];
3130 if ([href hasPrefix:@"apptapp://package/"]) {
3131 NSString *name = [href substringFromIndex:18];
3132 Package *package = [database_ packageWithName:name];
3133 if (package == nil) {
3134 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3135 initWithTitle:@"Cannot Locate Package"
3136 buttons:[NSArray arrayWithObjects:@"Close", nil]
3137 defaultButtonIndex:0
3142 [sheet setBodyText:[NSString stringWithFormat:
3143 @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
3146 [sheet popupAlertAnimated:YES];
3148 [self setBackButtonTitle:title_];
3149 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3150 [view setDelegate:delegate_];
3151 [view setPackage:package];
3152 [book_ pushPage:view];
3157 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3158 title_ = [title retain];
3159 [self setTitle:title];
3162 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3163 if ([frame parentFrame] != nil)
3168 [indicator_ startAnimation];
3169 [self reloadButtons];
3171 if (title_ != nil) {
3176 [self setTitle:@"Loading..."];
3178 WebView *webview = [webview_ webView];
3179 NSString *href = [webview mainFrameURL];
3180 [urls_ addObject:[NSURL URLWithString:href]];
3182 CGRect webrect = [scroller_ frame];
3183 webrect.size.height = 0;
3184 [webview_ setFrame:webrect];
3187 - (void) _finishLoading {
3190 [indicator_ stopAnimation];
3191 [self reloadButtons];
3195 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3196 if ([frame parentFrame] != nil)
3198 [self _finishLoading];
3201 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3202 if ([frame parentFrame] != nil)
3204 [self setTitle:[error localizedDescription]];
3205 [self _finishLoading];
3208 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3209 if ((self = [super initWithBook:book]) != nil) {
3210 database_ = database;
3213 struct CGRect bounds = [self bounds];
3215 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3216 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3217 [self addSubview:pinstripe];
3219 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3220 [self addSubview:scroller_];
3222 [scroller_ setScrollingEnabled:YES];
3223 [scroller_ setAdjustForContentSizeChange:YES];
3224 [scroller_ setClipsSubviews:YES];
3225 [scroller_ setAllowsRubberBanding:YES];
3226 [scroller_ setScrollDecelerationFactor:0.99];
3227 [scroller_ setDelegate:self];
3229 CGRect webrect = [scroller_ bounds];
3230 webrect.size.height = 0;
3232 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3233 [scroller_ addSubview:webview_];
3235 [webview_ setTilingEnabled:YES];
3236 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3237 [webview_ setAutoresizes:YES];
3238 [webview_ setDelegate:self];
3239 //[webview_ setEnabledGestures:2];
3241 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3242 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3243 [indicator_ setStyle:0];
3245 Package *package([database_ packageWithName:@"cydia"]);
3246 NSString *application = package == nil ? @"Cydia" : [NSString
3247 stringWithFormat:@"Cydia/%@",
3251 WebView *webview = [webview_ webView];
3252 [webview setApplicationNameForUserAgent:application];
3253 [webview setFrameLoadDelegate:self];
3254 [webview setResourceLoadDelegate:self];
3255 [webview setUIDelegate:self];
3257 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3261 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3265 - (void) _leftButtonClicked {
3266 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3267 initWithTitle:@"About Cydia Packager"
3268 buttons:[NSArray arrayWithObjects:@"Close", nil]
3269 defaultButtonIndex:0
3275 @"Copyright (C) 2008\n"
3276 "Jay Freeman (saurik)\n"
3277 "saurik@saurik.com\n"
3278 "http://www.saurik.com/\n"
3281 "http://www.theokorigroup.com/\n"
3283 "College of Creative Studies,\n"
3284 "University of California,\n"
3286 "http://www.ccs.ucsb.edu/"
3289 [sheet popupAlertAnimated:YES];
3292 - (void) _rightButtonClicked {
3297 - (NSString *) leftButtonTitle {
3301 - (NSString *) rightButtonTitle {
3302 return loading_ ? @"" : @"Reload";
3305 - (NSString *) title {
3309 - (NSString *) backButtonTitle {
3313 - (void) setPageActive:(BOOL)active {
3315 [book_ addSubview:indicator_];
3317 [indicator_ removeFromSuperview];
3320 - (void) resetViewAnimated:(BOOL)animated {
3326 @interface CYBook : RVBook <
3329 _transient Database *database_;
3331 UIProgressIndicator *indicator_;
3332 UITextLabel *prompt_;
3333 UIProgressBar *progress_;
3337 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3343 /* Install View {{{ */
3344 @interface InstallView : RVPage {
3345 _transient Database *database_;
3346 NSMutableArray *packages_;
3347 NSMutableArray *sections_;
3351 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3352 - (void) reloadData;
3356 @implementation InstallView
3359 [list_ setDataSource:nil];
3360 [list_ setDelegate:nil];
3362 [packages_ release];
3363 [sections_ release];
3368 - (int) numberOfRowsInTable:(UITable *)table {
3369 return [sections_ count] + 1;
3372 - (float) table:(UITable *)table heightForRow:(int)row {
3376 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3378 reusing = [[[SectionCell alloc] init] autorelease];
3379 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3383 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3387 - (void) tableRowSelected:(NSNotification *)notification {
3388 int row = [[notification object] selectedRow];
3397 title = @"All Packages";
3399 section = [sections_ objectAtIndex:(row - 1)];
3400 title = [section name];
3403 PackageTable *table = [[[PackageTable alloc]
3407 filter:@selector(isUninstalledInSection:)
3408 with:(section == nil ? nil : [section name])
3411 [table setDelegate:delegate_];
3413 [book_ pushPage:table];
3416 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3417 if ((self = [super initWithBook:book]) != nil) {
3418 database_ = database;
3420 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3421 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3423 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3424 [self addSubview:list_];
3426 UITableColumn *column = [[[UITableColumn alloc]
3427 initWithTitle:@"Name"
3429 width:[self frame].size.width
3432 [list_ setDataSource:self];
3433 [list_ setSeparatorStyle:1];
3434 [list_ addTableColumn:column];
3435 [list_ setDelegate:self];
3436 [list_ setReusesTableCells:YES];
3442 - (void) reloadData {
3443 NSArray *packages = [database_ packages];
3445 [packages_ removeAllObjects];
3446 [sections_ removeAllObjects];
3448 for (size_t i(0); i != [packages count]; ++i) {
3449 Package *package([packages objectAtIndex:i]);
3450 if ([package installed] == nil)
3451 [packages_ addObject:package];
3454 [packages_ sortUsingSelector:@selector(compareBySection:)];
3456 Section *section = nil;
3457 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3458 Package *package = [packages_ objectAtIndex:offset];
3459 NSString *name = [package section];
3461 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3462 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3463 [sections_ addObject:section];
3466 [section addToCount];
3472 - (void) resetViewAnimated:(BOOL)animated {
3473 [list_ resetViewAnimated:animated];
3476 - (NSString *) title {
3480 - (NSString *) backButtonTitle {
3486 /* Changes View {{{ */
3487 @interface ChangesView : RVPage {
3488 _transient Database *database_;
3489 NSMutableArray *packages_;
3490 NSMutableArray *sections_;
3491 UISectionList *list_;
3495 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3496 - (void) reloadData;
3500 @implementation ChangesView
3503 [[list_ table] setDelegate:nil];
3504 [list_ setDataSource:nil];
3506 [packages_ release];
3507 [sections_ release];
3512 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3513 return [sections_ count];
3516 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3517 return [[sections_ objectAtIndex:section] name];
3520 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3521 return [[sections_ objectAtIndex:section] row];
3524 - (int) numberOfRowsInTable:(UITable *)table {
3525 return [packages_ count];
3528 - (float) table:(UITable *)table heightForRow:(int)row {
3532 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3534 reusing = [[[PackageCell alloc] init] autorelease];
3535 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3539 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3543 - (void) tableRowSelected:(NSNotification *)notification {
3544 int row = [[notification object] selectedRow];
3547 Package *package = [packages_ objectAtIndex:row];
3548 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3549 [view setDelegate:delegate_];
3550 [view setPackage:package];
3551 [book_ pushPage:view];
3554 - (void) _leftButtonClicked {
3555 [(CYBook *)book_ update];
3556 [self reloadButtons];
3559 - (void) _rightButtonClicked {
3560 [delegate_ distUpgrade];
3563 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3564 if ((self = [super initWithBook:book]) != nil) {
3565 database_ = database;
3567 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3568 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3570 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3571 [self addSubview:list_];
3573 [list_ setShouldHideHeaderInShortLists:NO];
3574 [list_ setDataSource:self];
3575 //[list_ setSectionListStyle:1];
3577 UITableColumn *column = [[[UITableColumn alloc]
3578 initWithTitle:@"Name"
3580 width:[self frame].size.width
3583 UITable *table = [list_ table];
3584 [table setSeparatorStyle:1];
3585 [table addTableColumn:column];
3586 [table setDelegate:self];
3587 [table setReusesTableCells:YES];
3593 - (void) reloadData {
3594 NSArray *packages = [database_ packages];
3596 [packages_ removeAllObjects];
3597 [sections_ removeAllObjects];
3599 for (size_t i(0); i != [packages count]; ++i) {
3600 Package *package([packages objectAtIndex:i]);
3601 if ([package installed] == nil || [package upgradable])
3602 [packages_ addObject:package];
3605 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3607 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3608 Section *section = nil;
3611 bool unseens = false;
3613 CFLocaleRef locale = CFLocaleCopyCurrent();
3614 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3616 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3617 Package *package = [packages_ objectAtIndex:offset];
3619 if ([package upgradable]) {
3621 [upgradable addToCount];
3624 NSDate *seen = [package seen];
3629 name = [@"n/a ?" retain];
3631 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3634 if (section == nil || ![[section name] isEqualToString:name]) {
3635 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3636 [sections_ addObject:section];
3640 [section addToCount];
3644 CFRelease(formatter);
3648 Section *last = [sections_ lastObject];
3649 size_t count = [last count];
3650 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3651 [sections_ removeLastObject];
3655 [sections_ insertObject:upgradable atIndex:0];
3658 [self reloadButtons];
3661 - (void) resetViewAnimated:(BOOL)animated {
3662 [list_ resetViewAnimated:animated];
3665 - (NSString *) leftButtonTitle {
3666 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3669 - (NSString *) rightButtonTitle {
3670 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3673 - (NSString *) title {
3679 /* Manage View {{{ */
3680 @interface ManageView : PackageTable {
3683 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3687 @implementation ManageView
3689 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3694 filter:@selector(isInstalledInSection:)
3700 - (NSString *) title {
3701 return @"Installed Packages";
3704 - (NSString *) backButtonTitle {
3705 return @"All Packages";
3710 /* Search View {{{ */
3711 @protocol SearchViewDelegate
3712 - (void) showKeyboard:(BOOL)show;
3715 @interface SearchView : RVPage {
3717 UISearchField *field_;
3718 UITransitionView *transition_;
3719 PackageTable *table_;
3720 UIPreferencesTable *advanced_;
3725 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3726 - (void) reloadData;
3730 @implementation SearchView
3734 [[field_ textTraits] setEditingDelegate:nil];
3736 [field_ setDelegate:nil];
3738 [accessory_ release];
3740 [transition_ release];
3742 [advanced_ release];
3747 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3748 [delegate_ showKeyboard:YES];
3749 [table_ setEnabled:NO];
3750 [self addSubview:dimmed_];
3753 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3754 [dimmed_ removeFromSuperview];
3755 [table_ setEnabled:YES];
3756 [delegate_ showKeyboard:NO];
3759 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3760 NSString *text([field_ text]);
3761 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3764 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3765 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3769 [field_ resignFirstResponder];
3773 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3774 if ((self = [super initWithBook:book]) != nil) {
3775 CGRect pageBounds = [book_ pageBounds];
3777 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
3778 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3779 [self addSubview:pinstripe];*/
3781 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3782 CGColor dimmed(space_, 0, 0, 0, 0.5);
3783 [dimmed_ setBackgroundColor:dimmed];
3785 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3786 [self addSubview:transition_];
3788 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3790 table_ = [[PackageTable alloc]
3794 filter:@selector(isSearchedForBy:)
3798 [transition_ transition:0 toView:table_];
3800 CGRect cnfrect = {{1, 38}, {17, 18}};
3803 area.origin.x = cnfrect.size.width + 15;
3805 area.size.width = [self bounds].size.width - area.origin.x - 18;
3806 area.size.height = [UISearchField defaultHeight];
3808 field_ = [[UISearchField alloc] initWithFrame:area];
3810 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3811 [field_ setFont:font];
3814 [field_ setPlaceholder:@"Package Names & Descriptions"];
3815 [field_ setPaddingTop:5];
3816 [field_ setDelegate:self];
3819 UITextTraits *traits = [field_ textTraits];
3820 [traits setEditingDelegate:self];
3821 [traits setReturnKeyType:6];
3822 [traits setAutoCapsType:0];
3823 [traits setAutoCorrectionType:1];
3826 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3827 [configure setShowPressFeedback:YES];
3828 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
3829 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3831 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3832 [accessory_ addSubview:field_];
3833 [accessory_ addSubview:configure];
3838 LKAnimation *animation = [LKTransition animation];
3839 [animation setType:@"oglFlip"];
3840 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3841 [animation setFillMode:@"extended"];
3842 [animation setTransitionFlags:3];
3843 [animation setDuration:10];
3844 [animation setSpeed:0.35];
3845 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
3846 [[transition_ _layer] addAnimation:animation forKey:0];
3847 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3848 flipped_ = !flipped_;
3851 - (void) configurePushed {
3852 [field_ resignFirstResponder];
3856 - (void) resetViewAnimated:(BOOL)animated {
3859 [table_ resetViewAnimated:animated];
3862 - (void) reloadData {
3865 [table_ setObject:[field_ text]];
3866 [table_ reloadData];
3867 [table_ resetCursor];
3870 - (UIView *) accessoryView {
3874 - (NSString *) title {
3878 - (NSString *) backButtonTitle {
3885 @implementation CYBook
3889 [indicator_ release];
3891 [progress_ release];
3900 [navbar_ setPrompt:@""];
3901 [navbar_ addSubview:overlay_];
3902 [indicator_ startAnimation];
3903 [prompt_ setText:@"Updating Database..."];
3904 [progress_ setProgress:0];
3909 detachNewThreadSelector:@selector(_update)
3918 [overlay_ removeFromSuperview];
3919 [indicator_ stopAnimation];
3920 [delegate_ reloadData];
3922 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3925 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3926 if ((self = [super initWithFrame:frame]) != nil) {
3927 database_ = database;
3929 CGRect ovrrect = [navbar_ bounds];
3930 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3932 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3934 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3935 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3936 CGRect indrect = {{indoffset, indoffset}, indsize};
3938 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3939 [indicator_ setStyle:2];
3940 [overlay_ addSubview:indicator_];
3942 CGSize prmsize = {200, indsize.width};
3945 indoffset * 2 + indsize.width,
3946 (ovrrect.size.height - prmsize.height) / 2
3949 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3951 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3953 [prompt_ setColor:White_];
3954 [prompt_ setBackgroundColor:Clear_];
3955 [prompt_ setFont:font];
3959 [overlay_ addSubview:prompt_];
3961 CGSize prgsize = {75, 100};
3964 ovrrect.size.width - prgsize.width - 10,
3965 (ovrrect.size.height - prgsize.height) / 2
3968 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3969 [progress_ setStyle:0];
3970 [overlay_ addSubview:progress_];
3975 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3978 status.setDelegate(self);
3980 [database_ updateWithStatus:status];
3983 performSelectorOnMainThread:@selector(_update_)
3991 - (void) setProgressError:(NSString *)error {
3993 performSelectorOnMainThread:@selector(_setProgressError:)
3999 - (void) setProgressTitle:(NSString *)title {
4001 performSelectorOnMainThread:@selector(_setProgressTitle:)
4007 - (void) setProgressPercent:(float)percent {
4010 - (void) addProgressOutput:(NSString *)output {
4012 performSelectorOnMainThread:@selector(_addProgressOutput:)
4018 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4022 - (void) _setProgressError:(NSString *)error {
4023 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4026 - (void) _setProgressTitle:(NSString *)title {
4027 [prompt_ setText:[title stringByAppendingString:@"..."]];
4030 - (void) _addProgressOutput:(NSString *)output {
4035 @interface Cydia : UIApplication <
4036 ConfirmationViewDelegate,
4037 ProgressViewDelegate,
4046 UIButtonBar *buttonbar_;
4048 ConfirmationView *confirm_;
4050 Database *database_;
4051 ProgressView *progress_;
4055 UIKeyboard *keyboard_;
4060 @implementation Cydia
4062 - (void) _reloadData {
4063 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4064 [hud setText:@"Reloading Data"];
4065 [overlay_ addSubview:hud];
4068 [database_ reloadData];
4072 if (Packages_ == nil) {
4073 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
4074 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4079 NSArray *packages = [database_ packages];
4080 for (int i(0), e([packages count]); i != e; ++i) {
4081 Package *package = [packages objectAtIndex:i];
4082 if ([package upgradable])
4087 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4088 [buttonbar_ setBadgeValue:badge forButton:3];
4089 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4090 [buttonbar_ setBadgeAnimated:YES forButton:3];
4091 [self setApplicationBadge:badge];
4093 [buttonbar_ setBadgeValue:nil forButton:3];
4094 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4095 [buttonbar_ setBadgeAnimated:NO forButton:3];
4096 [self removeApplicationBadge];
4099 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4103 [hud removeFromSuperview];*/
4106 - (void) reloadData {
4107 @synchronized (self) {
4108 if (confirm_ == nil)
4114 pkgProblemResolver *resolver = [database_ resolver];
4116 resolver->InstallProtect();
4117 if (!resolver->Resolve(true))
4122 [database_ prepare];
4124 if ([database_ cache]->BrokenCount() == 0)
4125 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4127 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4128 NSArray *packages = [database_ packages];
4130 for (size_t i(0); i != [packages count]; ++i) {
4131 Package *package = [packages objectAtIndex:i];
4132 if ([package broken])
4133 [broken addObject:[package name]];
4136 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4137 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4138 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4139 defaultButtonIndex:0
4144 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4145 [sheet popupAlertAnimated:YES];
4151 - (void) installPackage:(Package *)package {
4152 @synchronized (self) {
4159 - (void) removePackage:(Package *)package {
4160 @synchronized (self) {
4167 - (void) distUpgrade {
4168 @synchronized (self) {
4169 [database_ upgrade];
4175 @synchronized (self) {
4183 [overlay_ removeFromSuperview];
4187 detachNewThreadSelector:@selector(perform)
4194 - (void) bootstrap_ {
4196 [database_ upgrade];
4197 [database_ prepare];
4198 [database_ perform];
4201 - (void) bootstrap {
4203 detachNewThreadSelector:@selector(bootstrap_)
4206 title:@"Bootstrap Install..."
4210 - (void) progressViewIsComplete:(ProgressView *)progress {
4211 @synchronized (self) {
4214 if (confirm_ != nil) {
4215 [underlay_ addSubview:overlay_];
4216 [confirm_ removeFromSuperview];
4223 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4227 - (void) setPage:(RVPage *)page {
4228 [page setDelegate:self];
4229 [book_ setPage:page];
4232 - (RVPage *) _setNewsPage {
4233 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4234 [self setPage:browser];
4235 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4239 - (void) buttonBarItemTapped:(id)sender {
4240 unsigned tag = [sender tag];
4242 [book_ resetViewAnimated:YES];
4248 [self _setNewsPage];
4252 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4256 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4260 [self setPage:[[[ManageView alloc] initWithBook:book_ database:database_] autorelease]];
4264 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4274 - (void) applicationWillSuspend {
4275 [super applicationWillSuspend];
4278 if (FW_LEAST(1,1,3))
4279 notify_post("com.apple.language.changed");
4281 system("launchctl stop com.apple.SpringBoard");
4284 - (void) applicationDidFinishLaunching:(id)unused {
4285 _assert(pkgInitConfig(*_config));
4286 _assert(pkgInitSystem(*_config, _system));
4291 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4292 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4294 [window_ orderFront: self];
4295 [window_ makeKey: self];
4296 [window_ _setHidden: NO];
4298 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
4299 [window_ setContentView:progress_];
4301 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4302 [progress_ setContentView:underlay_];
4304 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4307 [underlay_ addSubview:overlay_];
4309 database_ = [[Database alloc] init];
4310 [database_ setDelegate:progress_];
4312 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4313 0, 0, screenrect.size.width, screenrect.size.height - 48
4314 ) database:database_];
4316 [book_ setDelegate:self];
4318 [overlay_ addSubview:book_];
4320 NSArray *buttonitems = [NSArray arrayWithObjects:
4321 [NSDictionary dictionaryWithObjectsAndKeys:
4322 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4323 @"news-up.png", kUIButtonBarButtonInfo,
4324 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4325 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4326 self, kUIButtonBarButtonTarget,
4327 @"News", kUIButtonBarButtonTitle,
4328 @"0", kUIButtonBarButtonType,
4331 [NSDictionary dictionaryWithObjectsAndKeys:
4332 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4333 @"install-up.png", kUIButtonBarButtonInfo,
4334 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4335 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4336 self, kUIButtonBarButtonTarget,
4337 @"Install", kUIButtonBarButtonTitle,
4338 @"0", kUIButtonBarButtonType,
4341 [NSDictionary dictionaryWithObjectsAndKeys:
4342 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4343 @"changes-up.png", kUIButtonBarButtonInfo,
4344 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4345 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4346 self, kUIButtonBarButtonTarget,
4347 @"Changes", kUIButtonBarButtonTitle,
4348 @"0", kUIButtonBarButtonType,
4351 [NSDictionary dictionaryWithObjectsAndKeys:
4352 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4353 @"manage-up.png", kUIButtonBarButtonInfo,
4354 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4355 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4356 self, kUIButtonBarButtonTarget,
4357 @"Manage", kUIButtonBarButtonTitle,
4358 @"0", kUIButtonBarButtonType,
4361 [NSDictionary dictionaryWithObjectsAndKeys:
4362 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4363 @"search-up.png", kUIButtonBarButtonInfo,
4364 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4365 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4366 self, kUIButtonBarButtonTarget,
4367 @"Search", kUIButtonBarButtonTitle,
4368 @"0", kUIButtonBarButtonType,
4372 buttonbar_ = [[UIButtonBar alloc]
4374 withFrame:CGRectMake(
4375 0, screenrect.size.height - 48,
4376 screenrect.size.width, 48
4378 withItemList:buttonitems
4381 [buttonbar_ setDelegate:self];
4382 [buttonbar_ setBarStyle:1];
4383 [buttonbar_ setButtonBarTrackingMode:2];
4385 int buttons[5] = {1, 2, 3, 4, 5};
4386 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4387 [buttonbar_ showButtonGroup:0 withDuration:0];
4389 for (int i = 0; i != 5; ++i)
4390 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4391 i * 64 + 2, 1, 60, 48
4394 [buttonbar_ showSelectionForButton:1];
4395 [overlay_ addSubview:buttonbar_];
4397 [UIKeyboard initImplementationNow];
4398 CGSize keysize = [UIKeyboard defaultSize];
4399 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4400 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4401 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4406 [progress_ resetView];
4411 [self _setNewsPage];
4414 - (void) showKeyboard:(BOOL)show {
4416 [overlay_ addSubview:keyboard_];
4418 [keyboard_ removeFromSuperview];
4421 - (void) slideUp:(UIAlertSheet *)alert {
4422 [alert presentSheetFromButtonBar:buttonbar_];
4427 void AddPreferences(NSString *plist) {
4428 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4430 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4431 _assert(settings != NULL);
4432 NSMutableArray *items = [settings objectForKey:@"items"];
4436 for (size_t i(0); i != [items count]; ++i) {
4437 NSMutableDictionary *item([items objectAtIndex:i]);
4438 NSString *label = [item objectForKey:@"label"];
4439 if (label != nil && [label isEqualToString:@"Cydia"]) {
4446 for (size_t i(0); i != [items count]; ++i) {
4447 NSDictionary *item([items objectAtIndex:i]);
4448 NSString *label = [item objectForKey:@"label"];
4449 if (label != nil && [label isEqualToString:@"General"]) {
4450 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4451 @"CydiaSettings", @"bundle",
4452 @"PSLinkCell", @"cell",
4453 [NSNumber numberWithBool:YES], @"hasIcon",
4454 [NSNumber numberWithBool:YES], @"isController",
4456 nil] atIndex:(i + 1)];
4462 _assert([settings writeToFile:plist atomically:YES] == YES);
4469 id Alloc_(id self, SEL selector) {
4470 id object = alloc_(self, selector);
4471 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4476 id Dealloc_(id self, SEL selector) {
4477 id object = dealloc_(self, selector);
4478 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4482 int main(int argc, char *argv[]) {
4483 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4485 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4487 Home_ = NSHomeDirectory();
4490 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4491 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4492 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4493 Sounds_Keyboard_ = [keyboard boolValue];
4499 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4500 alloc_ = alloc->method_imp;
4501 alloc->method_imp = (IMP) &Alloc_;*/
4503 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4504 dealloc_ = dealloc->method_imp;
4505 dealloc->method_imp = (IMP) &Dealloc_;*/
4507 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4508 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4509 Firmware_ = strdup([prover UTF8String]);
4510 NSArray *versions = [prover componentsSeparatedByString:@"."];
4511 int count = [versions count];
4512 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4513 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4514 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4519 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4520 char *machine = new char[size];
4521 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4524 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4525 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4526 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4527 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4531 IOObjectRelease(service);
4534 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4535 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4537 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4538 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4540 Packages_ = [Metadata_ objectForKey:@"Packages"];
4542 setenv("CYDIA", "", _not(int));
4543 if (access("/User", F_OK) != 0)
4544 system("/usr/libexec/cydia/firmware.sh");
4545 system("dpkg --configure -a");
4547 space_ = CGColorSpaceCreateDeviceRGB();
4549 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4550 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4551 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4552 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4554 int value = UIApplicationMain(argc, argv, [Cydia class]);
4556 CGColorSpaceRelease(space_);