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>
52 #include <ext/stdio_filebuf.h>
54 #include <apt-pkg/acquire.h>
55 #include <apt-pkg/acquire-item.h>
56 #include <apt-pkg/algorithms.h>
57 #include <apt-pkg/cachefile.h>
58 #include <apt-pkg/configuration.h>
59 #include <apt-pkg/debmetaindex.h>
60 #include <apt-pkg/error.h>
61 #include <apt-pkg/init.h>
62 #include <apt-pkg/pkgrecords.h>
63 #include <apt-pkg/sourcelist.h>
64 #include <apt-pkg/sptr.h>
66 #include <sys/sysctl.h>
70 #include <mach-o/nlist.h>
80 /* Extension Keywords {{{ */
81 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
83 #define _assert(test) do \
85 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
90 #define _not(type) ((type) ~ (type) 0)
95 /* Miscellaneous Messages {{{ */
96 @interface NSString (Cydia)
97 - (NSString *) stringByAddingPercentEscapes;
98 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
101 /* External Constants {{{ */
102 extern NSString *kUIButtonBarButtonAction;
103 extern NSString *kUIButtonBarButtonInfo;
104 extern NSString *kUIButtonBarButtonInfoOffset;
105 extern NSString *kUIButtonBarButtonSelectedInfo;
106 extern NSString *kUIButtonBarButtonStyle;
107 extern NSString *kUIButtonBarButtonTag;
108 extern NSString *kUIButtonBarButtonTarget;
109 extern NSString *kUIButtonBarButtonTitle;
110 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
111 extern NSString *kUIButtonBarButtonTitleWidth;
112 extern NSString *kUIButtonBarButtonType;
119 #define $_ fprintf(stderr, "+");_trace();
120 #define _$ fprintf(stderr, "-");_trace();
123 /* iPhoneOS 2.0 Compatibility {{{ */
125 @interface UICGColor : NSObject {
128 - (id) initWithCGColor:(CGColorRef)color;
134 - (UIFont *) fontWithSize:(CGFloat)size;
137 @interface NSObject (iPhoneOS)
138 - (CGColorRef) cgColor;
139 - (CGColorRef) CGColor;
143 @implementation NSObject (iPhoneOS)
145 - (CGColorRef) cgColor {
146 return [self CGColor];
149 - (CGColorRef) CGColor {
150 return (CGColorRef) self;
154 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
159 @interface UITextView (iPhoneOS)
160 - (void) setTextSize:(float)size;
163 @implementation UITextView (iPhoneOS)
165 - (void) setTextSize:(float)size {
166 [self setFont:[[self font] fontWithSize:size]];
173 OBJC_EXPORT const char *class_getName(Class cls);
175 /* Reset View (UIView) {{{ */
176 @interface UIView (RVBook)
177 - (void) resetViewAnimated:(BOOL)animated;
181 @implementation UIView (RVBook)
183 - (void) resetViewAnimated:(BOOL)animated {
184 fprintf(stderr, "%s\n", class_getName(self->isa));
189 fprintf(stderr, "%s\n", class_getName(self->isa));
195 /* Reset View (UITable) {{{ */
196 @interface UITable (RVBook)
197 - (void) resetViewAnimated:(BOOL)animated;
201 @implementation UITable (RVBook)
203 - (void) resetViewAnimated:(BOOL)animated {
204 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
213 /* Reset View (UISectionList) {{{ */
214 @interface UISectionList (RVBook)
215 - (void) resetViewAnimated:(BOOL)animated;
219 @implementation UISectionList (RVBook)
221 - (void) resetViewAnimated:(BOOL)animated {
222 [[self table] resetViewAnimated:animated];
226 [[self table] clearView];
232 /* Perl-Compatible RegEx {{{ */
242 Pcre(const char *regex) :
247 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
250 fprintf(stderr, "%d:%s\n", offset, error);
254 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
255 matches_ = new int[(capture_ + 1) * 3];
263 NSString *operator [](size_t match) {
265 stringWithCString:(data_ + matches_[match * 2])
266 length:(matches_[match * 2 + 1] - matches_[match * 2])
270 bool operator ()(const char *data, size_t size) {
272 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
276 /* Mime Addresses {{{ */
277 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
279 @interface Address : NSObject {
285 - (NSString *) email;
287 + (Address *) addressWithString:(NSString *)string;
288 - (Address *) initWithString:(NSString *)string;
291 @implementation Address
300 - (NSString *) name {
304 - (NSString *) email {
308 + (Address *) addressWithString:(NSString *)string {
309 return [[[Address alloc] initWithString:string] autorelease];
312 - (Address *) initWithString:(NSString *)string {
313 if ((self = [super init]) != nil) {
314 const char *data = [string UTF8String];
315 size_t size = [string length];
317 if (email_r(data, size)) {
318 name_ = [email_r[1] retain];
319 email_ = [email_r[2] retain];
321 name_ = [[NSString stringWithCString:data length:size] retain];
329 /* CoreGraphics Primitives {{{ */
340 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
343 Set(space, red, green, blue, alpha);
348 CGColorRelease(color_);
355 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
357 float color[] = {red, green, blue, alpha};
358 color_ = CGColorCreate(space, color);
361 operator CGColorRef() {
376 /* Right Alignment {{{ */
377 @interface UIRightTextLabel : UITextLabel {
378 float _savedRightEdgeX;
379 BOOL _sizedtofit_flag;
382 - (void) setFrame:(CGRect)frame;
383 - (void) setText:(NSString *)text;
384 - (void) realignText;
387 @implementation UIRightTextLabel
389 - (void) setFrame:(CGRect)frame {
390 [super setFrame:frame];
391 if (_sizedtofit_flag == NO) {
392 _savedRightEdgeX = frame.origin.x;
397 - (void) setText:(NSString *)text {
398 [super setText:text];
402 - (void) realignText {
403 CGRect oldFrame = [self frame];
405 _sizedtofit_flag = YES;
406 [self sizeToFit]; // shrink down size so I can right align it
408 CGRect newFrame = [self frame];
410 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
411 oldFrame.size.width = newFrame.size.width;
412 [super setFrame:oldFrame];
413 _sizedtofit_flag = NO;
419 /* Random Global Variables {{{ */
420 static const int PulseInterval_ = 50000;
422 static CGColor Black_;
423 static CGColor Clear_;
425 static CGColor White_;
427 static NSString *Home_;
428 static BOOL Sounds_Keyboard_;
430 const char *Firmware_ = NULL;
431 const char *Machine_ = NULL;
432 const char *SerialNumber_ = NULL;
438 CGColorSpaceRef space_;
440 #define FW_LEAST(major, minor, bugfix) \
441 (major < Major_ || major == Major_ && \
442 (minor < Minor_ || minor == Minor_ && \
448 static NSMutableDictionary *Metadata_;
449 static NSMutableDictionary *Packages_;
452 NSString *GetLastUpdate() {
453 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
456 return @"Never or Unknown";
458 CFLocaleRef locale = CFLocaleCopyCurrent();
459 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
460 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
462 CFRelease(formatter);
465 return [(NSString *) formatted autorelease];
468 /* Display Helpers {{{ */
469 inline float Interpolate(float begin, float end, float fraction) {
470 return (end - begin) * fraction + begin;
473 NSString *SizeString(double size) {
475 while (size > 1024) {
480 static const char *powers_[] = {"B", "kB", "MB", "GB"};
482 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
485 static const float TextViewOffset_ = 22;
487 UITextView *GetTextView(NSString *value, float left, bool html) {
488 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
489 [text setEditable:NO];
490 [text setTextSize:16];
492 [text setHTML:value];
494 [text setText:value];
495 [text setEnabled:NO];
497 [text setBackgroundColor:Clear_];
499 CGRect frame = [text frame];
500 [text setFrame:frame];
501 CGRect rect = [text visibleTextRect];
502 frame.size.height = rect.size.height;
503 [text setFrame:frame];
512 @protocol ProgressDelegate
513 - (void) setProgressError:(NSString *)error;
514 - (void) setProgressTitle:(NSString *)title;
515 - (void) setProgressPercent:(float)percent;
516 - (void) addProgressOutput:(NSString *)output;
519 @protocol CydiaDelegate
520 - (void) installPackage:(Package *)package;
521 - (void) removePackage:(Package *)package;
522 - (void) slideUp:(UIAlertSheet *)alert;
523 - (void) distUpgrade;
526 /* Status Delegation {{{ */
528 public pkgAcquireStatus
531 _transient id<ProgressDelegate> delegate_;
539 void setDelegate(id delegate) {
540 delegate_ = delegate;
543 virtual bool MediaChange(std::string media, std::string drive) {
547 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
550 virtual void Fetch(pkgAcquire::ItemDesc &item) {
551 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
554 virtual void Done(pkgAcquire::ItemDesc &item) {
557 virtual void Fail(pkgAcquire::ItemDesc &item) {
559 item.Owner->Status == pkgAcquire::Item::StatIdle ||
560 item.Owner->Status == pkgAcquire::Item::StatDone
564 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
567 virtual bool Pulse(pkgAcquire *Owner) {
568 bool value = pkgAcquireStatus::Pulse(Owner);
571 double(CurrentBytes + CurrentItems) /
572 double(TotalBytes + TotalItems)
575 [delegate_ setProgressPercent:percent];
579 virtual void Start() {
582 virtual void Stop() {
586 /* Progress Delegation {{{ */
591 _transient id<ProgressDelegate> delegate_;
594 virtual void Update() {
595 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
596 [delegate_ setProgressPercent:(Percent / 100)];
605 void setDelegate(id delegate) {
606 delegate_ = delegate;
609 virtual void Done() {
610 [delegate_ setProgressPercent:1];
615 /* Database Interface {{{ */
616 @interface Database : NSObject {
618 pkgRecords *records_;
619 pkgProblemResolver *resolver_;
620 pkgAcquire *fetcher_;
622 SPtr<pkgPackageManager> manager_;
623 pkgSourceList *list_;
625 NSMutableDictionary *sources_;
626 NSMutableArray *packages_;
628 _transient id delegate_;
634 - (void) _readStatus:(NSNumber *)fd;
635 - (void) _readOutput:(NSNumber *)fd;
637 - (Package *) packageWithName:(NSString *)name;
640 - (pkgCacheFile &) cache;
641 - (pkgRecords *) records;
642 - (pkgProblemResolver *) resolver;
643 - (pkgAcquire &) fetcher;
644 - (NSArray *) packages;
652 - (void) updateWithStatus:(Status &)status;
654 - (void) setDelegate:(id)delegate;
655 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
659 /* Source Class {{{ */
660 @interface Source : NSObject {
661 NSString *description_;
666 NSString *distribution_;
670 NSString *defaultIcon_;
675 - (Source *) initWithMetaIndex:(metaIndex *)index;
680 - (NSString *) distribution;
683 - (NSString *) description;
684 - (NSString *) label;
685 - (NSString *) origin;
686 - (NSString *) version;
688 - (NSString *) defaultIcon;
691 @implementation Source
695 [distribution_ release];
698 if (description_ != nil)
699 [description_ release];
706 if (defaultIcon_ != nil)
707 [defaultIcon_ release];
712 - (Source *) initWithMetaIndex:(metaIndex *)index {
713 if ((self = [super init]) != nil) {
714 trusted_ = index->IsTrusted();
716 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
717 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
718 type_ = [[NSString stringWithCString:index->GetType()] retain];
726 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
727 if (dindex != NULL) {
728 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
730 while (std::getline(release, line)) {
731 std::string::size_type colon(line.find(':'));
732 if (colon == std::string::npos)
735 std::string name(line.substr(0, colon));
736 std::string value(line.substr(colon + 1));
737 while (!value.empty() && value[0] == ' ')
738 value = value.substr(1);
740 if (name == "Default-Icon")
741 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
742 else if (name == "Description")
743 description_ = [[NSString stringWithCString:value.c_str()] retain];
744 else if (name == "Label")
745 label_ = [[NSString stringWithCString:value.c_str()] retain];
746 else if (name == "Origin")
747 origin_ = [[NSString stringWithCString:value.c_str()] retain];
748 else if (name == "Version")
749 version_ = [[NSString stringWithCString:value.c_str()] retain];
763 - (NSString *) distribution {
764 return distribution_;
767 - (NSString *) type {
771 - (NSString *) description {
775 - (NSString *) label {
779 - (NSString *) origin {
783 - (NSString *) version {
787 - (NSString *) defaultIcon {
793 /* Package Class {{{ */
794 NSString *Scour(const char *field, const char *begin, const char *end) {
795 size_t i(0), l(strlen(field));
798 const char *name = begin + i;
799 const char *colon = name + l;
800 const char *value = colon + 1;
805 memcmp(name, field, l) == 0
807 while (value != end && value[0] == ' ')
809 const char *line = std::find(value, end, '\n');
810 while (line != value && line[-1] == ' ')
812 return [NSString stringWithCString:value length:(line - value)];
814 begin = std::find(begin, end, '\n');
822 @interface Package : NSObject {
823 pkgCache::PkgIterator iterator_;
824 _transient Database *database_;
825 pkgCache::VerIterator version_;
826 pkgCache::VerFileIterator file_;
830 NSString *installed_;
839 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
840 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
842 - (NSString *) section;
843 - (Address *) maintainer;
845 - (NSString *) description;
846 - (NSString *) index;
850 - (NSString *) latest;
851 - (NSString *) installed;
858 - (NSString *) tagline;
860 - (NSString *) website;
864 - (BOOL) matches:(NSString *)text;
866 - (NSComparisonResult) compareByName:(Package *)package;
867 - (NSComparisonResult) compareBySection:(Package *)package;
868 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
869 - (NSComparisonResult) compareForChanges:(Package *)package;
874 - (NSNumber *) isSearchedForBy:(NSString *)search;
875 - (NSNumber *) isInstalledInSection:(NSString *)section;
876 - (NSNumber *) isUninstalledInSection:(NSString *)section;
880 @implementation Package
884 if (installed_ != nil)
885 [installed_ release];
901 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
902 if ((self = [super init]) != nil) {
903 iterator_ = iterator;
904 database_ = database;
909 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
911 const char *begin, *end;
912 parser->GetRec(begin, end);
914 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
915 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
917 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
918 name_ = Scour("Name", begin, end);
920 name_ = [name_ retain];
921 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
922 icon_ = Scour("Icon", begin, end);
924 icon_ = [icon_ retain];
925 website_ = Scour("Website", begin, end);
927 website_ = [website_ retain];
929 source_ = [[database_ getSource:file_.File()] retain];
931 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
932 if (metadata == nil || [metadata count] == 0) {
933 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
937 [Packages_ setObject:metadata forKey:id_];
942 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
943 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
944 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
945 return [[[Package alloc]
946 initWithIterator:iterator
954 - (NSString *) section {
955 const char *section = iterator_.Section();
956 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
959 - (Address *) maintainer {
960 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
961 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
965 return version_->InstalledSize;
968 - (NSString *) description {
969 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
970 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
972 NSArray *lines = [description componentsSeparatedByString:@"\n"];
973 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
974 if ([lines count] < 2)
977 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
978 for (size_t i(1); i != [lines count]; ++i) {
979 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
980 [trimmed addObject:trim];
983 return [trimmed componentsJoinedByString:@"\n"];
986 - (NSString *) index {
987 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
988 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
992 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
995 - (NSString *) latest {
999 - (NSString *) installed {
1003 - (BOOL) upgradable {
1004 if (NSString *installed = [self installed])
1005 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1007 return [self essential];
1010 - (BOOL) essential {
1011 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1015 return (*[database_ cache])[iterator_].InstBroken();
1022 - (NSString *) name {
1023 return name_ == nil ? id_ : name_;
1026 - (NSString *) tagline {
1030 - (NSString *) icon {
1034 - (NSString *) website {
1038 - (Source *) source {
1042 - (BOOL) matches:(NSString *)text {
1048 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1049 if (range.location != NSNotFound)
1052 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1053 if (range.location != NSNotFound)
1056 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1057 if (range.location != NSNotFound)
1063 - (NSComparisonResult) compareByName:(Package *)package {
1064 NSString *lhs = [self name];
1065 NSString *rhs = [package name];
1067 if ([lhs length] != 0 && [rhs length] != 0) {
1068 unichar lhc = [lhs characterAtIndex:0];
1069 unichar rhc = [rhs characterAtIndex:0];
1071 if (isalpha(lhc) && !isalpha(rhc))
1072 return NSOrderedAscending;
1073 else if (!isalpha(lhc) && isalpha(rhc))
1074 return NSOrderedDescending;
1077 return [lhs caseInsensitiveCompare:rhs];
1080 - (NSComparisonResult) compareBySection:(Package *)package {
1081 NSString *lhs = [self section];
1082 NSString *rhs = [package section];
1084 if (lhs == NULL && rhs != NULL)
1085 return NSOrderedAscending;
1086 else if (lhs != NULL && rhs == NULL)
1087 return NSOrderedDescending;
1088 else if (lhs != NULL && rhs != NULL) {
1089 NSComparisonResult result = [lhs compare:rhs];
1090 if (result != NSOrderedSame)
1094 return NSOrderedSame;
1097 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1098 NSString *lhs = [self section];
1099 NSString *rhs = [package section];
1101 if (lhs == NULL && rhs != NULL)
1102 return NSOrderedAscending;
1103 else if (lhs != NULL && rhs == NULL)
1104 return NSOrderedDescending;
1105 else if (lhs != NULL && rhs != NULL) {
1106 NSComparisonResult result = [lhs compare:rhs];
1107 if (result != NSOrderedSame)
1111 return [self compareByName:package];
1114 - (NSComparisonResult) compareForChanges:(Package *)package {
1115 BOOL lhs = [self upgradable];
1116 BOOL rhs = [package upgradable];
1119 return lhs ? NSOrderedAscending : NSOrderedDescending;
1121 switch ([[self seen] compare:[package seen]]) {
1122 case NSOrderedAscending:
1123 return NSOrderedDescending;
1126 case NSOrderedDescending:
1127 return NSOrderedAscending;
1133 return [self compareByName:package];
1137 pkgProblemResolver *resolver = [database_ resolver];
1138 resolver->Clear(iterator_);
1139 resolver->Protect(iterator_);
1140 pkgCacheFile &cache([database_ cache]);
1141 cache->MarkInstall(iterator_, false);
1142 pkgDepCache::StateCache &state((*cache)[iterator_]);
1143 if (!state.Install())
1144 cache->SetReInstall(iterator_, true);
1148 pkgProblemResolver *resolver = [database_ resolver];
1149 resolver->Clear(iterator_);
1150 resolver->Protect(iterator_);
1151 resolver->Remove(iterator_);
1152 [database_ cache]->MarkDelete(iterator_, true);
1155 - (NSNumber *) isSearchedForBy:(NSString *)search {
1156 return [NSNumber numberWithBool:[self matches:search]];
1159 - (NSNumber *) isInstalledInSection:(NSString *)section {
1160 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1163 - (NSNumber *) isUninstalledInSection:(NSString *)section {
1164 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1169 /* Section Class {{{ */
1170 @interface Section : NSObject {
1176 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1177 - (NSString *) name;
1180 - (void) addToCount;
1184 @implementation Section
1191 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1192 if ((self = [super init]) != nil) {
1193 name_ = [name retain];
1198 - (NSString *) name {
1210 - (void) addToCount {
1217 /* Database Implementation {{{ */
1218 @implementation Database
1225 - (void) _readStatus:(NSNumber *)fd {
1226 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1228 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1229 std::istream is(&ib);
1234 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1236 pcre_extra *study = NULL;
1238 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1239 int matches[(capture + 1) * 3];
1241 while (std::getline(is, line)) {
1242 const char *data(line.c_str());
1244 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1246 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1249 [delegate_ setProgressPercent:(percent / 100)];
1251 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1252 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1254 if (type == "pmerror")
1255 [delegate_ setProgressError:string];
1256 else if (type == "pmstatus")
1257 [delegate_ setProgressTitle:string];
1258 else if (type == "pmconffile")
1260 else _assert(false);
1267 - (void) _readOutput:(NSNumber *)fd {
1268 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1270 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1271 std::istream is(&ib);
1274 while (std::getline(is, line))
1275 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1281 - (Package *) packageWithName:(NSString *)name {
1282 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1283 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1286 - (Database *) init {
1287 if ((self = [super init]) != nil) {
1293 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1294 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1298 _assert(pipe(fds) != -1);
1302 detachNewThreadSelector:@selector(_readStatus:)
1304 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1307 _assert(pipe(fds) != -1);
1308 _assert(dup2(fds[1], 1) != -1);
1309 _assert(close(fds[1]) != -1);
1312 detachNewThreadSelector:@selector(_readOutput:)
1314 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1319 - (pkgCacheFile &) cache {
1323 - (pkgRecords *) records {
1327 - (pkgProblemResolver *) resolver {
1331 - (pkgAcquire &) fetcher {
1335 - (NSArray *) packages {
1339 - (void) reloadData {
1349 if (!cache_.Open(progress_, true)) {
1350 fprintf(stderr, "repairing corrupted database...\n");
1352 [self updateWithStatus:status_];
1353 _assert(cache_.Open(progress_, true));
1356 now_ = [[NSDate date] retain];
1358 records_ = new pkgRecords(cache_);
1359 resolver_ = new pkgProblemResolver(cache_);
1360 fetcher_ = new pkgAcquire(&status_);
1363 list_ = new pkgSourceList();
1364 _assert(list_->ReadMainList());
1366 [sources_ removeAllObjects];
1367 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1368 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1369 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1371 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1372 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1376 [packages_ removeAllObjects];
1377 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1378 if (Package *package = [Package packageWithIterator:iterator database:self])
1379 if ([package source] != nil || [package installed] != nil)
1380 [packages_ addObject:package];
1384 pkgRecords records(cache_);
1386 lock_ = new FileFd();
1387 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1388 _assert(!_error->PendingError());
1391 // XXX: explain this with an error message
1392 _assert(list.ReadMainList());
1394 manager_ = (_system->CreatePM(cache_));
1395 _assert(manager_->GetArchives(fetcher_, &list, &records));
1396 _assert(!_error->PendingError());
1400 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1404 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1406 if (result == pkgPackageManager::Failed)
1408 if (_error->PendingError())
1410 if (result != pkgPackageManager::Completed)
1415 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1416 _assert(pkgApplyStatus(cache_));
1418 if (cache_->BrokenCount() != 0) {
1419 _assert(pkgFixBroken(cache_));
1420 _assert(cache_->BrokenCount() == 0);
1421 _assert(pkgMinimizeUpgrade(cache_));
1424 _assert(pkgDistUpgrade(cache_));
1428 [self updateWithStatus:status_];
1431 - (void) updateWithStatus:(Status &)status {
1433 _assert(list.ReadMainList());
1436 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1437 _assert(!_error->PendingError());
1439 pkgAcquire fetcher(&status);
1440 _assert(list.GetIndexes(&fetcher));
1442 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1443 bool failed = false;
1444 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1445 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1446 (*item)->Finished();
1450 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1451 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1452 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1455 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1459 - (void) setDelegate:(id)delegate {
1460 delegate_ = delegate;
1461 status_.setDelegate(delegate);
1462 progress_.setDelegate(delegate);
1465 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1466 pkgIndexFile *index(NULL);
1467 list_->FindIndex(file, index);
1468 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1474 /* RVPage Interface {{{ */
1477 @interface RVPage : UIView {
1478 _transient RVBook *book_;
1479 _transient id delegate_;
1482 - (NSString *) title;
1483 - (NSString *) backButtonTitle;
1484 - (NSString *) rightButtonTitle;
1485 - (NSString *) leftButtonTitle;
1486 - (UIView *) accessoryView;
1488 - (void) _rightButtonClicked;
1489 - (void) _leftButtonClicked;
1491 - (void) setPageActive:(BOOL)active;
1492 - (void) resetViewAnimated:(BOOL)animated;
1494 - (void) setTitle:(NSString *)title;
1495 - (void) setBackButtonTitle:(NSString *)title;
1497 - (void) reloadButtons;
1498 - (void) reloadData;
1500 - (id) initWithBook:(RVBook *)book;
1502 - (void) setDelegate:(id)delegate;
1506 /* Reset View {{{ */
1507 @protocol RVDelegate
1508 - (void) setPageActive:(BOOL)active with:(id)object;
1509 - (void) resetViewAnimated:(BOOL)animated with:(id)object;
1510 - (void) reloadDataWith:(id)object;
1513 @interface RVBook : UIView {
1514 NSMutableArray *pages_;
1515 UINavigationBar *navbar_;
1516 UITransitionView *transition_;
1518 _transient id delegate_;
1521 - (id) initWithFrame:(CGRect)frame;
1522 - (void) setDelegate:(id)delegate;
1524 - (void) setPage:(RVPage *)page;
1526 - (void) pushPage:(RVPage *)page;
1527 - (void) popPages:(unsigned)pages;
1529 - (void) setPrompt:(NSString *)prompt;
1531 - (void) resetViewAnimated:(BOOL)animated;
1532 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
1534 - (void) setTitle:(NSString *)title forPage:(RVPage *)page;
1535 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
1536 - (void) reloadButtonsForPage:(RVPage *)page;
1538 - (void) reloadData;
1540 - (CGRect) pageBounds;
1544 @implementation RVBook
1547 [navbar_ setDelegate:nil];
1551 [transition_ release];
1555 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1556 _assert([pages_ count] != 0);
1557 RVPage *page = [pages_ lastObject];
1559 case 0: [page _rightButtonClicked]; break;
1560 case 1: [page _leftButtonClicked]; break;
1564 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
1565 _assert([pages_ count] != 0);
1567 [[pages_ lastObject] setPageActive:NO];
1568 [pages_ removeLastObject];
1570 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1573 - (id) initWithFrame:(CGRect)frame {
1574 if ((self = [super initWithFrame:frame]) != nil) {
1575 pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
1577 struct CGRect bounds = [self bounds];
1578 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
1579 CGRect navrect = {{0, 0}, navsize};
1581 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1582 [self addSubview:navbar_];
1584 [navbar_ setBarStyle:1];
1585 [navbar_ setDelegate:self];
1587 [navbar_ setPrompt:@""];
1589 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
1590 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
1593 [self addSubview:transition_];
1597 - (void) setDelegate:(id)delegate {
1598 delegate_ = delegate;
1601 - (void) setPage:(RVPage *)page {
1602 if ([pages_ count] != 0)
1603 [[pages_ lastObject] setPageActive:NO];
1605 [navbar_ disableAnimation];
1607 for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
1608 [navbar_ popNavigationItem];
1611 [self pushPage:page];
1612 [navbar_ enableAnimation];
1615 - (void) pushPage:(RVPage *)page {
1616 if ([pages_ count] != 0)
1617 [[pages_ lastObject] setPageActive:NO];
1619 NSString *title = [page title]; {
1620 const char *data = [title UTF8String];
1621 size_t size = [title length];
1623 Pcre title_r("^(.*?)( \\(.*\\))?$");
1624 if (title_r(data, size))
1628 NSString *backButtonTitle = [page backButtonTitle];
1629 if (backButtonTitle == nil)
1630 backButtonTitle = title;
1632 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1633 [navitem setBackButtonTitle:backButtonTitle];
1634 [navbar_ pushNavigationItem:navitem];
1636 BOOL animated = [pages_ count] == 0 ? NO : YES;
1637 [transition_ transition:(animated ? 1 : 0) toView:page];
1638 [page setPageActive:YES];
1640 [pages_ addObject:page];
1641 [self reloadButtonsForPage:page];
1643 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
1646 - (void) popPages:(unsigned)pages {
1650 [[pages_ lastObject] setPageActive:NO];
1653 for (unsigned i(0); i != pages; ++i)
1654 [navbar_ popNavigationItem];
1657 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1660 - (void) setPrompt:(NSString *)prompt {
1661 [navbar_ setPrompt:prompt];
1664 - (void) resetViewAnimated:(BOOL)animated {
1667 if ([pages_ count] > 1) {
1668 [navbar_ disableAnimation];
1669 while ([pages_ count] != (animated ? 2 : 1))
1670 [navbar_ popNavigationItem];
1671 [navbar_ enableAnimation];
1673 [navbar_ popNavigationItem];
1678 [self resetViewAnimated:animated toPage:[pages_ lastObject]];
1681 - (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
1682 [page resetViewAnimated:animated];
1683 [transition_ transition:(animated ? 2 : 0) toView:page];
1684 [page setPageActive:YES];
1685 [self reloadButtonsForPage:page];
1686 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
1689 - (void) setTitle:(NSString *)title forPage:(RVPage *)page {
1690 if ([pages_ count] == 0 || page != [pages_ lastObject])
1692 UINavigationItem *navitem = [navbar_ topItem];
1693 [navitem setTitle:title];
1696 - (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
1697 if ([pages_ count] == 0 || page != [pages_ lastObject])
1699 UINavigationItem *navitem = [navbar_ topItem];
1700 [navitem setBackButtonTitle:title];
1703 - (void) reloadButtonsForPage:(RVPage *)page {
1704 if ([pages_ count] == 0 || page != [pages_ lastObject])
1706 NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
1707 [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
1710 - (void) reloadData {
1711 for (int i(0), e([pages_ count]); i != e; ++i) {
1712 RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
1717 - (CGRect) pageBounds {
1718 return [transition_ bounds];
1723 /* RVPage Implementation {{{ */
1724 @implementation RVPage
1726 - (NSString *) title {
1727 [self doesNotRecognizeSelector:_cmd];
1731 - (NSString *) backButtonTitle {
1735 - (NSString *) leftButtonTitle {
1739 - (NSString *) rightButtonTitle {
1743 - (void) _rightButtonClicked {
1744 [self doesNotRecognizeSelector:_cmd];
1747 - (void) _leftButtonClicked {
1748 [self doesNotRecognizeSelector:_cmd];
1751 - (UIView *) accessoryView {
1755 - (void) setPageActive:(BOOL)active {
1758 - (void) resetViewAnimated:(BOOL)animated {
1759 [self doesNotRecognizeSelector:_cmd];
1762 - (void) setTitle:(NSString *)title {
1763 [book_ setTitle:title forPage:self];
1766 - (void) setBackButtonTitle:(NSString *)title {
1767 [book_ setBackButtonTitle:title forPage:self];
1770 - (void) reloadButtons {
1771 [book_ reloadButtonsForPage:self];
1774 - (void) reloadData {
1777 - (id) initWithBook:(RVBook *)book {
1778 if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
1783 - (void) setDelegate:(id)delegate {
1784 delegate_ = delegate;
1790 /* Confirmation View {{{ */
1791 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1792 if ([packages count] == 0)
1795 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1796 [fields setObject:text forKey:key];
1798 CGColor blue(space_, 0, 0, 0.4, 1);
1799 [text setTextColor:blue];
1802 @protocol ConfirmationViewDelegate
1807 @interface ConfirmationView : UIView {
1808 Database *database_;
1810 UITransitionView *transition_;
1812 UINavigationBar *navbar_;
1813 UIPreferencesTable *table_;
1814 NSMutableDictionary *fields_;
1815 UIAlertSheet *essential_;
1820 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1824 @implementation ConfirmationView
1827 [navbar_ setDelegate:nil];
1828 [transition_ setDelegate:nil];
1829 [table_ setDataSource:nil];
1831 [transition_ release];
1836 if (essential_ != nil)
1837 [essential_ release];
1842 [transition_ transition:7 toView:nil];
1846 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1847 if (from != nil && to == nil)
1848 [self removeFromSuperview];
1851 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1854 if (essential_ != nil)
1855 [essential_ popupAlertAnimated:YES];
1857 [delegate_ confirm];
1866 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1867 [essential_ dismiss];
1871 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1875 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1877 case 0: return @"Statistics";
1878 case 1: return @"Modifications";
1880 default: _assert(false);
1884 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1887 case 1: return [fields_ count];
1889 default: _assert(false);
1893 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1894 if (group != 1 || row == -1)
1897 _assert(size_t(row) < [fields_ count]);
1898 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1902 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1903 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1904 [cell setShowSelection:NO];
1907 case 0: switch (row) {
1909 [cell setTitle:@"Downloading"];
1910 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1914 [cell setTitle:@"Resuming At"];
1915 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1919 double size([database_ cache]->UsrSize());
1922 [cell setTitle:@"Disk Freeing"];
1923 [cell setValue:SizeString(-size)];
1925 [cell setTitle:@"Disk Using"];
1926 [cell setValue:SizeString(size)];
1930 default: _assert(false);
1934 _assert(size_t(row) < [fields_ count]);
1935 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1936 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1939 default: _assert(false);
1945 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1946 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1947 database_ = database;
1948 delegate_ = delegate;
1950 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1951 [self addSubview:transition_];
1953 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1955 CGSize navsize = [UINavigationBar defaultSize];
1956 CGRect navrect = {{0, 0}, navsize};
1957 CGRect bounds = [overlay_ bounds];
1959 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1960 [navbar_ setBarStyle:1];
1961 [navbar_ setDelegate:self];
1963 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1964 [navbar_ pushNavigationItem:navitem];
1965 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1967 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1969 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1970 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1971 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1972 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1973 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
1977 pkgCacheFile &cache([database_ cache]);
1978 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
1979 Package *package([Package packageWithIterator:iterator database:database_]);
1980 NSString *name([package name]);
1981 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
1982 pkgDepCache::StateCache &state(cache[iterator]);
1984 if (state.NewInstall())
1985 [installing addObject:name];
1986 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
1987 [reinstalling addObject:name];
1988 else if (state.Upgrade())
1989 [upgrading addObject:name];
1990 else if (state.Downgrade())
1991 [downgrading addObject:name];
1992 else if (state.Delete()) {
1995 [removing addObject:name];
2002 essential_ = [[UIAlertSheet alloc]
2003 initWithTitle:@"Unable to Comply"
2004 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2005 defaultButtonIndex:0
2010 [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."];
2013 AddTextView(fields_, installing, @"Installing");
2014 AddTextView(fields_, reinstalling, @"Reinstalling");
2015 AddTextView(fields_, upgrading, @"Upgrading");
2016 AddTextView(fields_, downgrading, @"Downgrading");
2017 AddTextView(fields_, removing, @"Removing");
2019 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2020 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2023 [table_ setReusesTableCells:YES];
2024 [table_ setDataSource:self];
2025 [table_ reloadData];
2027 [overlay_ addSubview:navbar_];
2028 [overlay_ addSubview:table_];
2030 [view addSubview:self];
2032 [transition_ setDelegate:self];
2034 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2035 [transition_ transition:0 toView:blank];
2036 [transition_ transition:3 toView:overlay_];
2043 /* Progress Data {{{ */
2044 @interface ProgressData : NSObject {
2050 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2057 @implementation ProgressData
2059 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2060 if ((self = [super init]) != nil) {
2061 selector_ = selector;
2081 /* Progress View {{{ */
2082 @interface ProgressView : UIView <
2086 UIView *background_;
2087 UITransitionView *transition_;
2089 UINavigationBar *navbar_;
2090 UIProgressBar *progress_;
2091 UITextView *output_;
2092 UITextLabel *status_;
2096 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2098 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
2099 - (void) setContentView:(UIView *)view;
2102 - (void) _retachThread;
2103 - (void) _detachNewThreadData:(ProgressData *)data;
2104 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2108 @protocol ProgressViewDelegate
2109 - (void) progressViewIsComplete:(ProgressView *)sender;
2112 @implementation ProgressView
2115 [transition_ setDelegate:nil];
2116 [navbar_ setDelegate:nil];
2119 if (background_ != nil)
2120 [background_ release];
2121 [transition_ release];
2124 [progress_ release];
2130 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2131 if (bootstrap_ && from == overlay_ && to == view_)
2135 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
2136 if ((self = [super initWithFrame:frame]) != nil) {
2137 delegate_ = delegate;
2139 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2140 [transition_ setDelegate:self];
2142 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2145 [overlay_ setBackgroundColor:Black_];
2147 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2148 [background_ setBackgroundColor:Black_];
2149 [self addSubview:background_];
2152 [self addSubview:transition_];
2154 CGSize navsize = [UINavigationBar defaultSize];
2155 CGRect navrect = {{0, 0}, navsize};
2157 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2158 [overlay_ addSubview:navbar_];
2160 [navbar_ setBarStyle:1];
2161 [navbar_ setDelegate:self];
2163 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2164 [navbar_ pushNavigationItem:navitem];
2166 CGRect bounds = [overlay_ bounds];
2167 CGSize prgsize = [UIProgressBar defaultSize];
2170 (bounds.size.width - prgsize.width) / 2,
2171 bounds.size.height - prgsize.height - 20
2174 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2175 [overlay_ addSubview:progress_];
2177 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2179 bounds.size.height - prgsize.height - 50,
2180 bounds.size.width - 20,
2184 [status_ setColor:White_];
2185 [status_ setBackgroundColor:Clear_];
2187 [status_ setCentersHorizontally:YES];
2188 //[status_ setFont:font];
2190 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2192 navrect.size.height + 20,
2193 bounds.size.width - 20,
2194 bounds.size.height - navsize.height - 62 - navrect.size.height
2197 //[output_ setTextFont:@"Courier New"];
2198 [output_ setTextSize:12];
2200 [output_ setTextColor:White_];
2201 [output_ setBackgroundColor:Clear_];
2203 [output_ setMarginTop:0];
2204 [output_ setAllowsRubberBanding:YES];
2206 [overlay_ addSubview:output_];
2207 [overlay_ addSubview:status_];
2209 [progress_ setStyle:0];
2213 - (void) setContentView:(UIView *)view {
2214 view_ = [view retain];
2217 - (void) resetView {
2218 [transition_ transition:6 toView:view_];
2221 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2225 - (void) _retachThread {
2226 [delegate_ progressViewIsComplete:self];
2230 - (void) _detachNewThreadData:(ProgressData *)data {
2231 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2233 [[data target] performSelector:[data selector] withObject:[data object]];
2236 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2241 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2242 [navbar_ popNavigationItem];
2243 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
2244 [navbar_ pushNavigationItem:navitem];
2246 [status_ setText:nil];
2247 [output_ setText:@""];
2248 [progress_ setProgress:0];
2250 [transition_ transition:6 toView:overlay_];
2253 detachNewThreadSelector:@selector(_detachNewThreadData:)
2255 withObject:[[ProgressData alloc]
2256 initWithSelector:selector
2263 - (void) setProgressError:(NSString *)error {
2265 performSelectorOnMainThread:@selector(_setProgressError:)
2271 - (void) setProgressTitle:(NSString *)title {
2273 performSelectorOnMainThread:@selector(_setProgressTitle:)
2279 - (void) setProgressPercent:(float)percent {
2281 performSelectorOnMainThread:@selector(_setProgressPercent:)
2282 withObject:[NSNumber numberWithFloat:percent]
2287 - (void) addProgressOutput:(NSString *)output {
2289 performSelectorOnMainThread:@selector(_addProgressOutput:)
2295 - (void) _setProgressError:(NSString *)error {
2296 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2297 initWithTitle:@"Package Error"
2298 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2299 defaultButtonIndex:0
2304 [sheet setBodyText:error];
2305 [sheet popupAlertAnimated:YES];
2308 - (void) _setProgressTitle:(NSString *)title {
2309 [status_ setText:[title stringByAppendingString:@"..."]];
2312 - (void) _setProgressPercent:(NSNumber *)percent {
2313 [progress_ setProgress:[percent floatValue]];
2316 - (void) _addProgressOutput:(NSString *)output {
2317 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2318 CGSize size = [output_ contentSize];
2319 CGRect rect = {{0, size.height}, {size.width, 0}};
2320 [output_ scrollRectToVisible:rect animated:YES];
2326 /* Package Cell {{{ */
2327 @interface PackageCell : UITableCell {
2330 UITextLabel *description_;
2331 UITextLabel *source_;
2332 UIImageView *trusted_;
2335 - (PackageCell *) init;
2336 - (void) setPackage:(Package *)package;
2338 - (void) _setSelected:(float)fraction;
2339 - (void) setSelected:(BOOL)selected;
2340 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2341 - (void) _setSelectionFadeFraction:(float)fraction;
2345 @implementation PackageCell
2350 [description_ release];
2356 - (PackageCell *) init {
2357 if ((self = [super init]) != nil) {
2358 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2359 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2360 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2362 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2363 [icon_ zoomToScale:0.5f];
2365 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2366 [name_ setBackgroundColor:Clear_];
2367 [name_ setFont:bold];
2369 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2370 [source_ setBackgroundColor:Clear_];
2371 [source_ setFont:large];
2373 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2374 [description_ setBackgroundColor:Clear_];
2375 [description_ setFont:small];
2377 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2378 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
2380 [self addSubview:icon_];
2381 [self addSubview:name_];
2382 [self addSubview:description_];
2383 [self addSubview:source_];
2391 - (void) setPackage:(Package *)package {
2392 Source *source = [package source];
2394 UIImage *image = nil;
2395 if (NSString *icon = [package icon])
2396 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2397 if (image == nil) if (NSString *icon = [source defaultIcon])
2398 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2400 image = [UIImage applicationImageNamed:@"unknown.png"];
2402 [icon_ setImage:image];
2403 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2405 [name_ setText:[package name]];
2406 [description_ setText:[package tagline]];
2411 if (source != nil) {
2412 label = [source label];
2413 trusted = [source trusted];
2414 } else if ([[package id] isEqualToString:@"firmware"]) {
2418 label = @"Unknown/Local";
2422 [source_ setText:[NSString stringWithFormat:@"from %@", label]];
2425 [self addSubview:trusted_];
2427 [trusted_ removeFromSuperview];
2430 - (void) _setSelected:(float)fraction {
2431 CGColor black(space_,
2432 Interpolate(0.0, 1.0, fraction),
2433 Interpolate(0.0, 1.0, fraction),
2434 Interpolate(0.0, 1.0, fraction),
2437 CGColor gray(space_,
2438 Interpolate(0.4, 1.0, fraction),
2439 Interpolate(0.4, 1.0, fraction),
2440 Interpolate(0.4, 1.0, fraction),
2443 [name_ setColor:black];
2444 [description_ setColor:gray];
2445 [source_ setColor:black];
2448 - (void) setSelected:(BOOL)selected {
2449 [self _setSelected:(selected ? 1.0 : 0.0)];
2450 [super setSelected:selected];
2453 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2455 [self _setSelected:(selected ? 1.0 : 0.0)];
2456 [super setSelected:selected withFade:fade];
2459 - (void) _setSelectionFadeFraction:(float)fraction {
2460 [self _setSelected:fraction];
2461 [super _setSelectionFadeFraction:fraction];
2466 /* Section Cell {{{ */
2467 @interface SectionCell : UITableCell {
2469 UITextLabel *count_;
2473 - (void) setSection:(Section *)section;
2475 - (void) _setSelected:(float)fraction;
2476 - (void) setSelected:(BOOL)selected;
2477 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2478 - (void) _setSelectionFadeFraction:(float)fraction;
2482 @implementation SectionCell
2491 if ((self = [super init]) != nil) {
2492 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2493 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2495 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2496 [name_ setBackgroundColor:Clear_];
2497 [name_ setFont:bold];
2499 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2500 [count_ setCentersHorizontally:YES];
2501 [count_ setBackgroundColor:Clear_];
2502 [count_ setFont:small];
2503 [count_ setColor:White_];
2505 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2506 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2508 [self addSubview:folder];
2509 [self addSubview:name_];
2510 [self addSubview:count_];
2512 [self _setSelected:0];
2519 - (void) setSection:(Section *)section {
2520 if (section == nil) {
2521 [name_ setText:@"All Packages"];
2522 [count_ setText:nil];
2524 NSString *name = [section name];
2525 [name_ setText:(name == nil ? @"(No Section)" : name)];
2526 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2530 - (void) _setSelected:(float)fraction {
2531 CGColor black(space_,
2532 Interpolate(0.0, 1.0, fraction),
2533 Interpolate(0.0, 1.0, fraction),
2534 Interpolate(0.0, 1.0, fraction),
2537 [name_ setColor:black];
2540 - (void) setSelected:(BOOL)selected {
2541 [self _setSelected:(selected ? 1.0 : 0.0)];
2542 [super setSelected:selected];
2545 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2547 [self _setSelected:(selected ? 1.0 : 0.0)];
2548 [super setSelected:selected withFade:fade];
2551 - (void) _setSelectionFadeFraction:(float)fraction {
2552 [self _setSelected:fraction];
2553 [super _setSelectionFadeFraction:fraction];
2559 /* Browser Interface {{{ */
2560 @interface BrowserView : RVPage {
2561 _transient Database *database_;
2562 UIScroller *scroller_;
2563 UIWebView *webview_;
2564 NSMutableArray *urls_;
2565 UIProgressIndicator *indicator_;
2571 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2572 - (void) loadURL:(NSURL *)url;
2574 - (void) loadRequest:(NSURLRequest *)request;
2577 - (WebView *) webView;
2579 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2584 /* Package View {{{ */
2585 @protocol PackageViewDelegate
2586 - (void) performPackage:(Package *)package;
2589 @interface PackageView : RVPage {
2590 _transient Database *database_;
2591 UIPreferencesTable *table_;
2594 UITextView *description_;
2597 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2598 - (void) setPackage:(Package *)package;
2602 @implementation PackageView
2605 [table_ setDataSource:nil];
2606 [table_ setDelegate:nil];
2608 if (package_ != nil)
2612 if (description_ != nil)
2613 [description_ release];
2618 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2619 return [package_ source] == nil ? 2 : 3;
2622 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2625 case 1: return @"Package Details";
2626 case 2: return @"Source Information";
2628 default: _assert(false);
2632 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2633 if (group != 0 || row != 1)
2636 return [description_ visibleTextRect].size.height + TextViewOffset_;
2639 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2641 case 0: return [package_ website] == nil ? 2 : 3;
2645 default: _assert(false);
2649 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2650 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2651 [cell setShowSelection:NO];
2654 case 0: switch (row) {
2656 [cell setTitle:[package_ name]];
2657 [cell setValue:[package_ latest]];
2661 [cell addSubview:description_];
2665 [cell setTitle:@"More Information"];
2666 [cell setShowDisclosure:YES];
2667 [cell setShowSelection:YES];
2670 default: _assert(false);
2673 case 1: switch (row) {
2675 [cell setTitle:@"Identifier"];
2676 [cell setValue:[package_ id]];
2680 [cell setTitle:@"Installed Version"];
2681 NSString *installed([package_ installed]);
2682 [cell setValue:(installed == nil ? @"n/a" : installed)];
2686 [cell setTitle:@"Section"];
2687 NSString *section([package_ section]);
2688 [cell setValue:(section == nil ? @"n/a" : section)];
2692 [cell setTitle:@"Expanded Size"];
2693 [cell setValue:SizeString([package_ size])];
2697 [cell setTitle:@"Maintainer"];
2698 [cell setValue:[[package_ maintainer] name]];
2699 [cell setShowDisclosure:YES];
2700 [cell setShowSelection:YES];
2703 default: _assert(false);
2706 case 2: switch (row) {
2708 [cell setTitle:[[package_ source] label]];
2709 [cell setValue:[[package_ source] version]];
2713 [cell setValue:[[package_ source] description]];
2717 [cell setTitle:@"Origin"];
2718 [cell setValue:[[package_ source] origin]];
2721 default: _assert(false);
2724 default: _assert(false);
2730 - (BOOL) canSelectRow:(int)row {
2734 - (void) tableRowSelected:(NSNotification *)notification {
2735 int row = [table_ selectedRow];
2736 NSString *website = [package_ website];
2738 if (row == (website == nil ? 8 : 9))
2739 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2740 [[package_ maintainer] email],
2741 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2743 else if (website != nil && row == 3) {
2744 NSURL *url = [NSURL URLWithString:website];
2745 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2746 [browser setDelegate:delegate_];
2747 [book_ pushPage:browser];
2748 [browser loadURL:url];
2752 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2754 case 1: [delegate_ installPackage:package_]; break;
2755 case 2: [delegate_ removePackage:package_]; break;
2761 - (void) _rightButtonClicked {
2762 if ([package_ installed] == nil)
2763 [delegate_ installPackage:package_];
2765 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2767 if ([package_ upgradable])
2768 [buttons addObject:@"Upgrade"];
2770 [buttons addObject:@"Reinstall"];
2772 [buttons addObject:@"Remove"];
2773 [buttons addObject:@"Cancel"];
2775 [delegate_ slideUp:[[[UIAlertSheet alloc]
2776 initWithTitle:@"Manage Package"
2778 defaultButtonIndex:2
2785 - (NSString *) rightButtonTitle {
2786 _assert(package_ != nil);
2787 return [package_ installed] == nil ? @"Install" : @"Manage";
2790 - (NSString *) title {
2794 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2795 if ((self = [super initWithBook:book]) != nil) {
2796 database_ = database;
2798 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2799 [self addSubview:table_];
2801 [table_ setDataSource:self];
2802 [table_ setDelegate:self];
2806 - (void) setPackage:(Package *)package {
2807 if (package_ != nil) {
2808 [package_ autorelease];
2817 if (description_ != nil) {
2818 [description_ release];
2822 if (package != nil) {
2823 package_ = [package retain];
2824 name_ = [[package id] retain];
2826 NSString *description([package description]);
2827 if (description == nil)
2828 description = [package tagline];
2829 description_ = [GetTextView(description, 12, true) retain];
2831 [description_ setTextColor:Black_];
2833 [table_ reloadData];
2837 - (void) resetViewAnimated:(BOOL)animated {
2838 [table_ resetViewAnimated:animated];
2841 - (void) reloadData {
2842 [self setPackage:[database_ packageWithName:name_]];
2843 [self reloadButtons];
2848 /* Package Table {{{ */
2849 @interface PackageTable : RVPage {
2850 _transient Database *database_;
2854 NSMutableArray *packages_;
2855 NSMutableArray *sections_;
2856 UISectionList *list_;
2859 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2861 - (void) setDelegate:(id)delegate;
2862 - (void) reloadData;
2866 @implementation PackageTable
2869 [list_ setDataSource:nil];
2874 [packages_ release];
2875 [sections_ release];
2880 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2881 return [sections_ count];
2884 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2885 return [[sections_ objectAtIndex:section] name];
2888 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2889 return [[sections_ objectAtIndex:section] row];
2892 - (int) numberOfRowsInTable:(UITable *)table {
2893 return [packages_ count];
2896 - (float) table:(UITable *)table heightForRow:(int)row {
2900 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2902 reusing = [[[PackageCell alloc] init] autorelease];
2903 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2907 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2911 - (void) tableRowSelected:(NSNotification *)notification {
2912 int row = [[notification object] selectedRow];
2916 Package *package = [packages_ objectAtIndex:row];
2917 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2918 [view setDelegate:delegate_];
2919 [view setPackage:package];
2920 [book_ pushPage:view];
2923 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2924 if ((self = [super initWithBook:book]) != nil) {
2925 database_ = database;
2926 title_ = [title retain];
2928 object_ = object == nil ? nil : [object retain];
2930 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2931 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2933 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2934 [list_ setDataSource:self];
2936 UITableColumn *column = [[[UITableColumn alloc]
2937 initWithTitle:@"Name"
2939 width:[self frame].size.width
2942 UITable *table = [list_ table];
2943 [table setSeparatorStyle:1];
2944 [table addTableColumn:column];
2945 [table setDelegate:self];
2946 [table setReusesTableCells:YES];
2948 [self addSubview:list_];
2953 - (void) setDelegate:(id)delegate {
2954 delegate_ = delegate;
2957 - (void) reloadData {
2958 NSArray *packages = [database_ packages];
2960 [packages_ removeAllObjects];
2961 [sections_ removeAllObjects];
2963 for (size_t i(0); i != [packages count]; ++i) {
2964 Package *package([packages objectAtIndex:i]);
2965 if ([[package performSelector:filter_ withObject:object_] boolValue])
2966 [packages_ addObject:package];
2969 [packages_ sortUsingSelector:@selector(compareByName:)];
2971 Section *section = nil;
2973 for (size_t offset(0); offset != [packages_ count]; ++offset) {
2974 Package *package = [packages_ objectAtIndex:offset];
2975 NSString *name = [package index];
2977 if (section == nil || ![[section name] isEqualToString:name]) {
2978 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2979 [sections_ addObject:section];
2982 [section addToCount];
2988 - (NSString *) title {
2992 - (void) resetViewAnimated:(BOOL)animated {
2993 [list_ resetViewAnimated:animated];
2999 /* Browser Implementation {{{ */
3000 @implementation BrowserView
3003 WebView *webview = [webview_ webView];
3004 [webview setFrameLoadDelegate:nil];
3005 [webview setResourceLoadDelegate:nil];
3006 [webview setUIDelegate:nil];
3008 [scroller_ setDelegate:nil];
3009 [webview_ setDelegate:nil];
3011 [scroller_ release];
3014 [indicator_ release];
3020 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3021 NSMutableURLRequest *request = [NSMutableURLRequest
3024 timeoutInterval:30.0
3027 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3028 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3029 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3031 [self loadRequest:request];
3035 - (void) loadURL:(NSURL *)url {
3036 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3039 // XXX: this needs to add the headers
3040 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3044 - (void) loadRequest:(NSURLRequest *)request {
3045 [webview_ loadRequest:request];
3048 - (void) reloadURL {
3049 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3050 [urls_ removeLastObject];
3051 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3054 - (WebView *) webView {
3055 return [webview_ webView];
3058 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3059 [scroller_ setContentSize:frame.size];
3062 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3063 [self view:sender didSetFrame:frame];
3066 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3067 return [self _addHeadersToRequest:request];
3070 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3071 [self setBackButtonTitle:title_];
3072 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3073 [browser setDelegate:delegate_];
3074 [book_ pushPage:browser];
3075 [browser loadRequest:[self _addHeadersToRequest:request]];
3076 return [browser webView];
3079 - (void) webView:(WebView *)sender willClickElement:(id)element {
3080 if (![element respondsToSelector:@selector(href)])
3082 NSString *href = [element href];
3085 if ([href hasPrefix:@"apptapp://package/"]) {
3086 NSString *name = [href substringFromIndex:18];
3087 Package *package = [database_ packageWithName:name];
3088 if (package == nil) {
3089 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3090 initWithTitle:@"Cannot Locate Package"
3091 buttons:[NSArray arrayWithObjects:@"Close", nil]
3092 defaultButtonIndex:0
3097 [sheet setBodyText:[NSString stringWithFormat:
3098 @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
3101 [sheet popupAlertAnimated:YES];
3103 [self setBackButtonTitle:title_];
3104 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3105 [view setDelegate:delegate_];
3106 [view setPackage:package];
3107 [book_ pushPage:view];
3112 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3113 title_ = [title retain];
3114 [self setTitle:title];
3117 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3118 if ([frame parentFrame] != nil)
3122 [indicator_ startAnimation];
3123 [self reloadButtons];
3125 if (title_ != nil) {
3130 [self setTitle:@"Loading..."];
3132 WebView *webview = [webview_ webView];
3133 NSString *href = [webview mainFrameURL];
3134 [urls_ addObject:[NSURL URLWithString:href]];
3136 CGRect webrect = [scroller_ frame];
3137 webrect.size.height = 0;
3138 [webview_ setFrame:webrect];
3141 - (void) _finishLoading {
3143 [indicator_ stopAnimation];
3144 [self reloadButtons];
3147 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3148 if ([frame parentFrame] != nil)
3150 [self _finishLoading];
3153 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3154 if ([frame parentFrame] != nil)
3156 [self setTitle:[error localizedDescription]];
3157 [self _finishLoading];
3160 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3161 if ((self = [super initWithBook:book]) != nil) {
3162 database_ = database;
3165 struct CGRect bounds = [self bounds];
3167 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3168 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3169 [self addSubview:pinstripe];
3171 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3172 [self addSubview:scroller_];
3174 [scroller_ setScrollingEnabled:YES];
3175 [scroller_ setAdjustForContentSizeChange:YES];
3176 [scroller_ setClipsSubviews:YES];
3177 [scroller_ setAllowsRubberBanding:YES];
3178 [scroller_ setScrollDecelerationFactor:0.99];
3179 [scroller_ setDelegate:self];
3181 CGRect webrect = [scroller_ bounds];
3182 webrect.size.height = 0;
3184 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3185 [scroller_ addSubview:webview_];
3187 [webview_ setTilingEnabled:YES];
3188 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3189 [webview_ setAutoresizes:YES];
3190 [webview_ setDelegate:self];
3191 //[webview_ setEnabledGestures:2];
3193 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3194 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3195 [indicator_ setStyle:0];
3197 Package *package([database_ packageWithName:@"cydia"]);
3198 NSString *application = package == nil ? @"Cydia" : [NSString
3199 stringWithFormat:@"Cydia/%@",
3203 WebView *webview = [webview_ webView];
3204 [webview setApplicationNameForUserAgent:application];
3205 [webview setFrameLoadDelegate:self];
3206 [webview setResourceLoadDelegate:self];
3207 [webview setUIDelegate:self];
3209 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3213 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3217 - (void) _leftButtonClicked {
3218 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3219 initWithTitle:@"About Cydia Packager"
3220 buttons:[NSArray arrayWithObjects:@"Close", nil]
3221 defaultButtonIndex:0
3227 @"Copyright (C) 2008\n"
3228 "Jay Freeman (saurik)\n"
3229 "saurik@saurik.com\n"
3230 "http://www.saurik.com/\n"
3233 "http://www.theokorigroup.com/\n"
3235 "College of Creative Studies,\n"
3236 "University of California,\n"
3238 "http://www.ccs.ucsb.edu/"
3241 [sheet popupAlertAnimated:YES];
3244 - (void) _rightButtonClicked {
3248 - (NSString *) leftButtonTitle {
3252 - (NSString *) rightButtonTitle {
3253 return loading_ ? @"" : @"Reload";
3256 - (NSString *) title {
3260 - (NSString *) backButtonTitle {
3264 - (void) setPageActive:(BOOL)active {
3266 [book_ addSubview:indicator_];
3268 [indicator_ removeFromSuperview];
3271 - (void) resetViewAnimated:(BOOL)animated {
3277 /* Install View {{{ */
3278 @interface InstallView : RVPage {
3279 _transient Database *database_;
3280 NSMutableArray *packages_;
3281 NSMutableArray *sections_;
3285 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3286 - (void) reloadData;
3290 @implementation InstallView
3293 [list_ setDataSource:nil];
3294 [list_ setDelegate:nil];
3296 [packages_ release];
3297 [sections_ release];
3302 - (int) numberOfRowsInTable:(UITable *)table {
3303 return [sections_ count] + 1;
3306 - (float) table:(UITable *)table heightForRow:(int)row {
3310 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3312 reusing = [[[SectionCell alloc] init] autorelease];
3313 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3317 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3321 - (void) tableRowSelected:(NSNotification *)notification {
3322 int row = [[notification object] selectedRow];
3331 title = @"All Packages";
3333 section = [sections_ objectAtIndex:(row - 1)];
3334 title = [section name];
3337 PackageTable *table = [[[PackageTable alloc]
3341 filter:@selector(isUninstalledInSection:)
3342 with:(section == nil ? nil : [section name])
3345 [table setDelegate:delegate_];
3347 [book_ pushPage:table];
3350 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3351 if ((self = [super initWithBook:book]) != nil) {
3352 database_ = database;
3354 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3355 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3357 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3358 [self addSubview:list_];
3360 UITableColumn *column = [[[UITableColumn alloc]
3361 initWithTitle:@"Name"
3363 width:[self frame].size.width
3366 [list_ setDataSource:self];
3367 [list_ setSeparatorStyle:1];
3368 [list_ addTableColumn:column];
3369 [list_ setDelegate:self];
3370 [list_ setReusesTableCells:YES];
3376 - (void) reloadData {
3377 NSArray *packages = [database_ packages];
3379 [packages_ removeAllObjects];
3380 [sections_ removeAllObjects];
3382 for (size_t i(0); i != [packages count]; ++i) {
3383 Package *package([packages objectAtIndex:i]);
3384 if ([package installed] == nil)
3385 [packages_ addObject:package];
3388 [packages_ sortUsingSelector:@selector(compareBySection:)];
3390 Section *section = nil;
3391 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3392 Package *package = [packages_ objectAtIndex:offset];
3393 NSString *name = [package section];
3395 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3396 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3397 [sections_ addObject:section];
3400 [section addToCount];
3406 - (void) resetViewAnimated:(BOOL)animated {
3407 [list_ resetViewAnimated:animated];
3410 - (NSString *) title {
3414 - (NSString *) backButtonTitle {
3420 /* Changes View {{{ */
3421 @interface ChangesView : RVPage {
3422 _transient Database *database_;
3423 NSMutableArray *packages_;
3424 NSMutableArray *sections_;
3425 UISectionList *list_;
3429 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3430 - (void) reloadData;
3434 @implementation ChangesView
3437 [[list_ table] setDelegate:nil];
3438 [list_ setDataSource:nil];
3440 [packages_ release];
3441 [sections_ release];
3446 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3447 return [sections_ count];
3450 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3451 return [[sections_ objectAtIndex:section] name];
3454 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3455 return [[sections_ objectAtIndex:section] row];
3458 - (int) numberOfRowsInTable:(UITable *)table {
3459 return [packages_ count];
3462 - (float) table:(UITable *)table heightForRow:(int)row {
3466 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3468 reusing = [[[PackageCell alloc] init] autorelease];
3469 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3473 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3477 - (void) tableRowSelected:(NSNotification *)notification {
3478 int row = [[notification object] selectedRow];
3481 Package *package = [packages_ objectAtIndex:row];
3482 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3483 [view setDelegate:delegate_];
3484 [view setPackage:package];
3485 [book_ pushPage:view];
3488 - (void) _rightButtonClicked {
3489 [delegate_ distUpgrade];
3492 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3493 if ((self = [super initWithBook:book]) != nil) {
3494 database_ = database;
3496 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3497 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3499 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3500 [self addSubview:list_];
3502 [list_ setShouldHideHeaderInShortLists:NO];
3503 [list_ setDataSource:self];
3504 //[list_ setSectionListStyle:1];
3506 UITableColumn *column = [[[UITableColumn alloc]
3507 initWithTitle:@"Name"
3509 width:[self frame].size.width
3512 UITable *table = [list_ table];
3513 [table setSeparatorStyle:1];
3514 [table addTableColumn:column];
3515 [table setDelegate:self];
3516 [table setReusesTableCells:YES];
3522 - (void) reloadData {
3523 NSArray *packages = [database_ packages];
3525 [packages_ removeAllObjects];
3526 [sections_ removeAllObjects];
3528 for (size_t i(0); i != [packages count]; ++i) {
3529 Package *package([packages objectAtIndex:i]);
3530 if ([package installed] == nil || [package upgradable])
3531 [packages_ addObject:package];
3534 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3536 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3537 Section *section = nil;
3540 bool unseens = false;
3542 CFLocaleRef locale = CFLocaleCopyCurrent();
3543 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3545 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3546 Package *package = [packages_ objectAtIndex:offset];
3548 if ([package upgradable]) {
3550 [upgradable addToCount];
3553 NSDate *seen = [package seen];
3558 name = [@"n/a ?" retain];
3560 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3563 if (section == nil || ![[section name] isEqualToString:name]) {
3564 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3565 [sections_ addObject:section];
3569 [section addToCount];
3573 CFRelease(formatter);
3577 Section *last = [sections_ lastObject];
3578 size_t count = [last count];
3579 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3580 [sections_ removeLastObject];
3584 [sections_ insertObject:upgradable atIndex:0];
3587 [self reloadButtons];
3590 - (void) resetViewAnimated:(BOOL)animated {
3591 [list_ resetViewAnimated:animated];
3594 - (NSString *) rightButtonTitle {
3595 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3598 - (NSString *) title {
3604 /* Search View {{{ */
3605 @protocol SearchViewDelegate
3606 - (void) showKeyboard:(BOOL)show;
3609 @interface SearchView : PackageTable {
3611 UISearchField *field_;
3614 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3615 - (void) reloadData;
3619 @implementation SearchView
3623 [[field_ textTraits] setEditingDelegate:nil];
3625 [field_ setDelegate:nil];
3627 [accessory_ release];
3632 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3633 [delegate_ showKeyboard:YES];
3634 [list_ setEnabled:NO];
3636 /*CGColor dimmed(alpha, 0, 0, 0, 0.5);
3637 [editor_ setBackgroundColor:dimmed];*/
3640 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3641 [list_ setEnabled:YES];
3642 [delegate_ showKeyboard:NO];
3645 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3646 NSString *text([field_ text]);
3647 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3650 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3651 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3655 [field_ resignFirstResponder];
3659 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3664 filter:@selector(isSearchedForBy:)
3667 CGRect cnfrect = {{0, 36}, {17, 18}};
3670 area.origin.x = cnfrect.size.width + 6;
3672 area.size.width = [self bounds].size.width - area.origin.x - 12;
3673 area.size.height = [UISearchField defaultHeight];
3675 field_ = [[UISearchField alloc] initWithFrame:area];
3677 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3678 [field_ setFont:font];
3681 [field_ setPlaceholder:@"Package Names & Descriptions"];
3682 [field_ setPaddingTop:5];
3683 [field_ setDelegate:self];
3686 UITextTraits *traits = [field_ textTraits];
3687 [traits setEditingDelegate:self];
3688 [traits setReturnKeyType:6];
3689 [traits setAutoCapsType:0];
3690 [traits setAutoCorrectionType:1];
3693 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3694 [configure setShowPressFeedback:YES];
3695 [configure setImage:[UIImage applicationImageNamed:@"configure.png"]];
3696 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3698 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3699 [accessory_ addSubview:field_];
3700 [accessory_ addSubview:configure];
3704 - (void) configurePushed {
3705 // XXX: implement flippy advanced panel
3708 - (void) reloadData {
3709 object_ = [[field_ text] retain];
3711 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3714 - (UIView *) accessoryView {
3718 - (NSString *) backButtonTitle {
3725 @interface CYBook : RVBook <
3728 _transient Database *database_;
3730 UIProgressIndicator *indicator_;
3731 UITextLabel *prompt_;
3732 UIProgressBar *progress_;
3735 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3740 @implementation CYBook
3744 [indicator_ release];
3746 [progress_ release];
3751 [navbar_ addSubview:overlay_];
3752 [indicator_ startAnimation];
3753 [prompt_ setText:@"Updating Database..."];
3754 [progress_ setProgress:0];
3757 detachNewThreadSelector:@selector(_update)
3764 [overlay_ removeFromSuperview];
3765 [indicator_ stopAnimation];
3766 [delegate_ reloadData];
3768 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3771 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3772 if ((self = [super initWithFrame:frame]) != nil) {
3773 database_ = database;
3775 CGRect ovrrect = [navbar_ bounds];
3776 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3778 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3780 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3781 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3782 CGRect indrect = {{indoffset, indoffset}, indsize};
3784 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3785 [indicator_ setStyle:2];
3786 [overlay_ addSubview:indicator_];
3788 CGSize prmsize = {200, indsize.width};
3791 indoffset * 2 + indsize.width,
3792 (ovrrect.size.height - prmsize.height) / 2
3795 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3797 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3799 [prompt_ setColor:White_];
3800 [prompt_ setBackgroundColor:Clear_];
3801 [prompt_ setFont:font];
3805 [overlay_ addSubview:prompt_];
3807 CGSize prgsize = {75, 100};
3810 ovrrect.size.width - prgsize.width - 10,
3811 (ovrrect.size.height - prgsize.height) / 2
3814 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3815 [progress_ setStyle:0];
3816 [overlay_ addSubview:progress_];
3821 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3824 status.setDelegate(self);
3826 [database_ updateWithStatus:status];
3829 performSelectorOnMainThread:@selector(_update_)
3837 - (void) setProgressError:(NSString *)error {
3839 performSelectorOnMainThread:@selector(_setProgressError:)
3845 - (void) setProgressTitle:(NSString *)title {
3847 performSelectorOnMainThread:@selector(_setProgressTitle:)
3853 - (void) setProgressPercent:(float)percent {
3856 - (void) addProgressOutput:(NSString *)output {
3858 performSelectorOnMainThread:@selector(_addProgressOutput:)
3864 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3868 - (void) _setProgressError:(NSString *)error {
3869 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
3872 - (void) _setProgressTitle:(NSString *)title {
3873 [prompt_ setText:[title stringByAppendingString:@"..."]];
3876 - (void) _addProgressOutput:(NSString *)output {
3881 @interface Cydia : UIApplication <
3882 ConfirmationViewDelegate,
3883 ProgressViewDelegate,
3892 UIButtonBar *buttonbar_;
3894 ConfirmationView *confirm_;
3896 Database *database_;
3897 ProgressView *progress_;
3901 UIKeyboard *keyboard_;
3906 @implementation Cydia
3908 - (void) _reloadData {
3909 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
3910 [hud setText:@"Reloading Data"];
3911 [overlay_ addSubview:hud];
3914 [database_ reloadData];
3918 if (Packages_ == nil) {
3919 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
3920 [Metadata_ setObject:Packages_ forKey:@"Packages"];
3925 NSArray *packages = [database_ packages];
3926 for (int i(0), e([packages count]); i != e; ++i) {
3927 Package *package = [packages objectAtIndex:i];
3928 if ([package upgradable])
3933 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
3934 [buttonbar_ setBadgeValue:badge forButton:3];
3935 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
3936 [buttonbar_ setBadgeAnimated:YES forButton:3];
3937 [self setApplicationBadge:badge];
3939 [buttonbar_ setBadgeValue:nil forButton:3];
3940 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
3941 [buttonbar_ setBadgeAnimated:NO forButton:3];
3942 [self removeApplicationBadge];
3945 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
3949 [hud removeFromSuperview];*/
3952 - (void) reloadData {
3953 @synchronized (self) {
3954 if (confirm_ == nil)
3960 pkgProblemResolver *resolver = [database_ resolver];
3962 resolver->InstallProtect();
3963 if (!resolver->Resolve(true))
3968 [database_ prepare];
3970 if ([database_ cache]->BrokenCount() == 0)
3971 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
3973 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
3974 NSArray *packages = [database_ packages];
3976 for (size_t i(0); i != [packages count]; ++i) {
3977 Package *package = [packages objectAtIndex:i];
3978 if ([package broken])
3979 [broken addObject:[package name]];
3982 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3983 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
3984 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3985 defaultButtonIndex:0
3990 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
3991 [sheet popupAlertAnimated:YES];
3997 - (void) installPackage:(Package *)package {
3998 @synchronized (self) {
4005 - (void) removePackage:(Package *)package {
4006 @synchronized (self) {
4013 - (void) distUpgrade {
4014 @synchronized (self) {
4015 [database_ upgrade];
4021 @synchronized (self) {
4029 [overlay_ removeFromSuperview];
4033 detachNewThreadSelector:@selector(perform)
4040 - (void) bootstrap_ {
4042 [database_ upgrade];
4043 [database_ prepare];
4044 [database_ perform];
4047 - (void) bootstrap {
4049 detachNewThreadSelector:@selector(bootstrap_)
4052 title:@"Bootstrap Install..."
4056 - (void) progressViewIsComplete:(ProgressView *)progress {
4057 @synchronized (self) {
4060 if (confirm_ != nil) {
4061 [underlay_ addSubview:overlay_];
4062 [confirm_ removeFromSuperview];
4069 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4073 - (void) setPage:(RVPage *)page {
4074 [page setDelegate:self];
4075 [book_ setPage:page];
4078 - (RVPage *) _setNewsPage {
4079 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4080 [self setPage:browser];
4081 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4085 - (void) buttonBarItemTapped:(id)sender {
4086 unsigned tag = [sender tag];
4090 [self _setNewsPage];
4094 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4098 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4102 [self setPage:[[[PackageTable alloc]
4106 filter:@selector(isInstalledInSection:)
4112 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4122 - (void) applicationWillSuspend {
4123 [super applicationWillSuspend];
4126 if (FW_LEAST(1,1,3))
4127 notify_post("com.apple.language.changed");
4129 system("launchctl stop com.apple.SpringBoard");
4132 - (void) applicationDidFinishLaunching:(id)unused {
4133 _assert(pkgInitConfig(*_config));
4134 _assert(pkgInitSystem(*_config, _system));
4139 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4140 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4142 [window_ orderFront: self];
4143 [window_ makeKey: self];
4144 [window_ _setHidden: NO];
4146 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
4147 [window_ setContentView:progress_];
4149 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4150 [progress_ setContentView:underlay_];
4152 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4155 [underlay_ addSubview:overlay_];
4157 database_ = [[Database alloc] init];
4158 [database_ setDelegate:progress_];
4160 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4161 0, 0, screenrect.size.width, screenrect.size.height - 48
4162 ) database:database_];
4164 [book_ setDelegate:self];
4166 [overlay_ addSubview:book_];
4168 NSArray *buttonitems = [NSArray arrayWithObjects:
4169 [NSDictionary dictionaryWithObjectsAndKeys:
4170 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4171 @"news-up.png", kUIButtonBarButtonInfo,
4172 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4173 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4174 self, kUIButtonBarButtonTarget,
4175 @"News", kUIButtonBarButtonTitle,
4176 @"0", kUIButtonBarButtonType,
4179 [NSDictionary dictionaryWithObjectsAndKeys:
4180 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4181 @"install-up.png", kUIButtonBarButtonInfo,
4182 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4183 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4184 self, kUIButtonBarButtonTarget,
4185 @"Install", kUIButtonBarButtonTitle,
4186 @"0", kUIButtonBarButtonType,
4189 [NSDictionary dictionaryWithObjectsAndKeys:
4190 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4191 @"changes-up.png", kUIButtonBarButtonInfo,
4192 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4193 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4194 self, kUIButtonBarButtonTarget,
4195 @"Changes", kUIButtonBarButtonTitle,
4196 @"0", kUIButtonBarButtonType,
4199 [NSDictionary dictionaryWithObjectsAndKeys:
4200 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4201 @"manage-up.png", kUIButtonBarButtonInfo,
4202 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4203 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4204 self, kUIButtonBarButtonTarget,
4205 @"Manage", kUIButtonBarButtonTitle,
4206 @"0", kUIButtonBarButtonType,
4209 [NSDictionary dictionaryWithObjectsAndKeys:
4210 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4211 @"search-up.png", kUIButtonBarButtonInfo,
4212 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4213 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4214 self, kUIButtonBarButtonTarget,
4215 @"Search", kUIButtonBarButtonTitle,
4216 @"0", kUIButtonBarButtonType,
4220 buttonbar_ = [[UIButtonBar alloc]
4222 withFrame:CGRectMake(
4223 0, screenrect.size.height - 48,
4224 screenrect.size.width, 48
4226 withItemList:buttonitems
4229 [buttonbar_ setDelegate:self];
4230 [buttonbar_ setBarStyle:1];
4231 [buttonbar_ setButtonBarTrackingMode:2];
4233 int buttons[5] = {1, 2, 3, 4, 5};
4234 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4235 [buttonbar_ showButtonGroup:0 withDuration:0];
4237 for (int i = 0; i != 5; ++i)
4238 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4239 i * 64 + 2, 1, 60, 48
4242 [buttonbar_ showSelectionForButton:1];
4243 [overlay_ addSubview:buttonbar_];
4245 [UIKeyboard initImplementationNow];
4246 CGSize keysize = [UIKeyboard defaultSize];
4247 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4248 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4249 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4254 [progress_ resetView];
4259 [self _setNewsPage];
4262 - (void) showKeyboard:(BOOL)show {
4264 [overlay_ addSubview:keyboard_];
4266 [keyboard_ removeFromSuperview];
4269 - (void) slideUp:(UIAlertSheet *)alert {
4270 [alert presentSheetFromButtonBar:buttonbar_];
4275 void AddPreferences(NSString *plist) {
4276 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4278 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4279 _assert(settings != NULL);
4280 NSMutableArray *items = [settings objectForKey:@"items"];
4284 for (size_t i(0); i != [items count]; ++i) {
4285 NSMutableDictionary *item([items objectAtIndex:i]);
4286 NSString *label = [item objectForKey:@"label"];
4287 if (label != nil && [label isEqualToString:@"Cydia"]) {
4294 for (size_t i(0); i != [items count]; ++i) {
4295 NSDictionary *item([items objectAtIndex:i]);
4296 NSString *label = [item objectForKey:@"label"];
4297 if (label != nil && [label isEqualToString:@"General"]) {
4298 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4299 @"CydiaSettings", @"bundle",
4300 @"PSLinkCell", @"cell",
4301 [NSNumber numberWithBool:YES], @"hasIcon",
4302 [NSNumber numberWithBool:YES], @"isController",
4304 nil] atIndex:(i + 1)];
4310 _assert([settings writeToFile:plist atomically:YES] == YES);
4317 id Alloc_(id self, SEL selector) {
4318 id object = alloc_(self, selector);
4319 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4324 id Dealloc_(id self, SEL selector) {
4325 id object = dealloc_(self, selector);
4326 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4330 int main(int argc, char *argv[]) {
4331 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4333 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4335 Home_ = NSHomeDirectory();
4338 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4339 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4340 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4341 Sounds_Keyboard_ = [keyboard boolValue];
4347 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4348 alloc_ = alloc->method_imp;
4349 alloc->method_imp = (IMP) &Alloc_;*/
4351 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4352 dealloc_ = dealloc->method_imp;
4353 dealloc->method_imp = (IMP) &Dealloc_;*/
4355 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4356 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4357 Firmware_ = strdup([prover UTF8String]);
4358 NSArray *versions = [prover componentsSeparatedByString:@"."];
4359 int count = [versions count];
4360 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4361 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4362 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4367 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4368 char *machine = new char[size];
4369 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4372 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4373 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4374 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4375 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4379 IOObjectRelease(service);
4382 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4383 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4385 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4386 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4388 Packages_ = [Metadata_ objectForKey:@"Packages"];
4390 setenv("CYDIA", "", _not(int));
4391 if (access("/User", F_OK) != 0)
4392 system("/usr/libexec/cydia/firmware.sh");
4393 system("dpkg --configure -a");
4395 space_ = CGColorSpaceCreateDeviceRGB();
4397 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4398 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4399 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4400 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4402 int value = UIApplicationMain(argc, argv, [Cydia class]);
4404 CGColorSpaceRelease(space_);