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.
39 #define UITextTraits UITextInputTraits
40 #define textTraits textInputTraits
41 #define setAutoCapsType setAutocapitalizationType
42 #define setAutoCorrectionType setAutocorrectionType
43 #define setPreferredKeyboardType setKeyboardType
46 /* #include Directives {{{ */
47 #include <objc/objc.h>
48 #include <objc/runtime.h>
50 #include <CoreGraphics/CoreGraphics.h>
51 #include <GraphicsServices/GraphicsServices.h>
52 #include <Foundation/Foundation.h>
53 #include <UIKit/UIKit.h>
54 #include <WebCore/DOMHTML.h>
56 #import "BrowserView.h"
58 #import "UICaboodle.h"
60 #include <WebKit/WebFrame.h>
61 #include <WebKit/WebView.h>
66 #include <ext/stdio_filebuf.h>
68 #include <apt-pkg/acquire.h>
69 #include <apt-pkg/acquire-item.h>
70 #include <apt-pkg/algorithms.h>
71 #include <apt-pkg/cachefile.h>
72 #include <apt-pkg/clean.h>
73 #include <apt-pkg/configuration.h>
74 #include <apt-pkg/debmetaindex.h>
75 #include <apt-pkg/error.h>
76 #include <apt-pkg/init.h>
77 #include <apt-pkg/pkgrecords.h>
78 #include <apt-pkg/sourcelist.h>
79 #include <apt-pkg/sptr.h>
81 #include <sys/sysctl.h>
85 #include <mach-o/nlist.h>
96 /* iPhoneOS 2.0 Compatibility {{{ */
98 @interface UICGColor : NSObject {
101 - (id) initWithCGColor:(CGColorRef)color;
107 + (id)systemFontOfSize:(float)fp8;
108 + (id)boldSystemFontOfSize:(float)fp8;
109 - (UIFont *) fontWithSize:(CGFloat)size;
112 @interface NSObject (iPhoneOS)
113 - (CGColorRef) cgColor;
114 - (CGColorRef) CGColor;
118 @implementation NSObject (iPhoneOS)
120 - (CGColorRef) cgColor {
121 return [self CGColor];
124 - (CGColorRef) CGColor {
125 return (CGColorRef) self;
129 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
134 @interface UITextView (iPhoneOS)
135 - (void) setTextSize:(float)size;
138 @implementation UITextView (iPhoneOS)
140 - (void) setTextSize:(float)size {
141 [self setFont:[[self font] fontWithSize:size]];
148 @interface UIApplication (IdleTimer)
149 - (void) setIdleTimerDisabled:(char)arg0;
154 kUIProgressIndicatorStyleMediumWhite = 1,
155 kUIProgressIndicatorStyleSmallWhite = 0,
156 kUIProgressIndicatorStyleSmallBlack = 4
157 } UIProgressIndicatorStyle;
160 kUIProgressIndicatorStyleMediumWhite = 0,
161 kUIProgressIndicatorStyleSmallWhite = 2,
162 kUIProgressIndicatorStyleSmallBlack = 3
163 } UIProgressIndicatorStyle;
167 kUIControlEventMouseDown = 1 << 0,
168 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
169 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
170 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
171 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
172 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
173 } UIControlEventMasks;
175 @interface NSString (UIKit)
176 - (NSString *) stringByAddingPercentEscapes;
177 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
180 @interface NSString (Cydia)
181 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
182 - (NSComparisonResult) compareByPath:(NSString *)other;
185 @implementation NSString (Cydia)
187 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
188 char data[length + 1];
189 memcpy(data, bytes, length);
191 return [NSString stringWithUTF8String:data];
194 - (NSComparisonResult) compareByPath:(NSString *)other {
195 NSString *prefix = [self commonPrefixWithString:other options:0];
196 size_t length = [prefix length];
198 NSRange lrange = NSMakeRange(length, [self length] - length);
199 NSRange rrange = NSMakeRange(length, [other length] - length);
201 lrange = [self rangeOfString:@"/" options:0 range:lrange];
202 rrange = [other rangeOfString:@"/" options:0 range:rrange];
204 NSComparisonResult value;
206 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
207 value = NSOrderedSame;
208 else if (lrange.location == NSNotFound)
209 value = NSOrderedAscending;
210 else if (rrange.location == NSNotFound)
211 value = NSOrderedDescending;
213 value = NSOrderedSame;
215 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
216 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
217 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
218 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
220 NSComparisonResult result = [lpath compare:rpath];
221 return result == NSOrderedSame ? value : result;
226 /* Perl-Compatible RegEx {{{ */
236 Pcre(const char *regex) :
241 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
244 fprintf(stderr, "%d:%s\n", offset, error);
248 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
249 matches_ = new int[(capture_ + 1) * 3];
257 NSString *operator [](size_t match) {
258 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
261 bool operator ()(NSString *data) {
262 // XXX: length is for characters, not for bytes
263 return operator ()([data UTF8String], [data length]);
266 bool operator ()(const char *data, size_t size) {
268 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
272 /* Mime Addresses {{{ */
273 @interface Address : NSObject {
279 - (NSString *) email;
281 + (Address *) addressWithString:(NSString *)string;
282 - (Address *) initWithString:(NSString *)string;
285 @implementation Address
294 - (NSString *) name {
298 - (NSString *) email {
302 + (Address *) addressWithString:(NSString *)string {
303 return [[[Address alloc] initWithString:string] autorelease];
306 - (Address *) initWithString:(NSString *)string {
307 if ((self = [super init]) != nil) {
308 const char *data = [string UTF8String];
309 size_t size = [string length];
311 static Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
313 if (email_r(data, size)) {
314 name_ = [email_r[1] retain];
315 email_ = [email_r[2] retain];
317 name_ = [[NSString alloc]
320 encoding:kCFStringEncodingUTF8
330 /* CoreGraphics Primitives {{{ */
341 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
344 Set(space, red, green, blue, alpha);
349 CGColorRelease(color_);
356 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
358 float color[] = {red, green, blue, alpha};
359 color_ = CGColorCreate(space, color);
362 operator CGColorRef() {
377 /* Right Alignment {{{ */
378 @interface UIRightTextLabel : UITextLabel {
379 float _savedRightEdgeX;
380 BOOL _sizedtofit_flag;
383 - (void) setFrame:(CGRect)frame;
384 - (void) setText:(NSString *)text;
385 - (void) realignText;
388 @implementation UIRightTextLabel
390 - (void) setFrame:(CGRect)frame {
391 [super setFrame:frame];
392 if (_sizedtofit_flag == NO) {
393 _savedRightEdgeX = frame.origin.x;
398 - (void) setText:(NSString *)text {
399 [super setText:text];
403 - (void) realignText {
404 CGRect oldFrame = [self frame];
406 _sizedtofit_flag = YES;
407 [self sizeToFit]; // shrink down size so I can right align it
409 CGRect newFrame = [self frame];
411 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
412 oldFrame.size.width = newFrame.size.width;
413 [super setFrame:oldFrame];
414 _sizedtofit_flag = NO;
420 extern "C" void UISetColor(CGColorRef color);
422 /* Random Global Variables {{{ */
423 static const int PulseInterval_ = 50000;
424 static const int ButtonBarHeight_ = 48;
425 static const float KeyboardTime_ = 0.3f;
426 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
432 static CGColor Blue_;
433 static CGColor Blueish_;
434 static CGColor Black_;
435 static CGColor Clear_;
437 static CGColor White_;
438 static CGColor Gray_;
440 static NSString *Home_;
441 static BOOL Sounds_Keyboard_;
443 static BOOL Advanced_;
444 //static BOOL Loaded_;
445 static BOOL Ignored_;
447 static UIFont *Font12_;
448 static UIFont *Font12Bold_;
449 static UIFont *Font14_;
450 static UIFont *Font18Bold_;
451 static UIFont *Font22Bold_;
453 const char *Firmware_ = NULL;
454 const char *Machine_ = NULL;
455 const char *SerialNumber_ = NULL;
462 CGColorSpaceRef space_;
464 #define FW_LEAST(major, minor, bugfix) \
465 (major < Major_ || major == Major_ && \
466 (minor < Minor_ || minor == Minor_ && \
472 static NSDictionary *SectionMap_;
473 static NSMutableDictionary *Metadata_;
474 static _transient NSMutableDictionary *Settings_;
475 static _transient NSString *Role_;
476 static _transient NSMutableDictionary *Packages_;
477 static _transient NSMutableDictionary *Sections_;
478 static _transient NSMutableDictionary *Sources_;
479 static bool Changed_;
482 NSString *GetLastUpdate() {
483 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
486 return @"Never or Unknown";
488 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
489 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
491 CFRelease(formatter);
493 return [(NSString *) formatted autorelease];
496 /* Display Helpers {{{ */
497 inline float Interpolate(float begin, float end, float fraction) {
498 return (end - begin) * fraction + begin;
501 NSString *SizeString(double size) {
503 while (size > 1024) {
508 static const char *powers_[] = {"B", "kB", "MB", "GB"};
510 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
513 NSString *StripVersion(NSString *version) {
514 NSRange colon = [version rangeOfString:@":"];
515 if (colon.location != NSNotFound)
516 version = [version substringFromIndex:(colon.location + 1)];
520 static const float TextViewOffset_ = 22;
522 UITextView *GetTextView(NSString *value, float left, bool html) {
523 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
524 [text setEditable:NO];
525 [text setTextSize:16];
527 [text setHTML:value];
529 [text setText:value];
530 [text setEnabled:NO];
532 [text setBackgroundColor:Clear_];
534 CGRect frame = [text frame];
535 [text setFrame:frame];
536 CGRect rect = [text visibleTextRect];
537 frame.size.height = rect.size.height;
538 [text setFrame:frame];
543 NSString *Simplify(NSString *title) {
544 const char *data = [title UTF8String];
545 size_t size = [title length];
547 static Pcre square_r("^\\[(.*)\\]$");
548 if (square_r(data, size))
549 return Simplify(square_r[1]);
551 static Pcre paren_r("^\\((.*)\\)$");
552 if (paren_r(data, size))
553 return Simplify(paren_r[1]);
555 static Pcre title_r("^(.*?) \\(.*\\)$");
556 if (title_r(data, size))
557 return Simplify(title_r[1]);
563 bool isSectionVisible(NSString *section) {
564 NSDictionary *metadata = [Sections_ objectForKey:section];
565 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
566 return hidden == nil || ![hidden boolValue];
569 /* Delegate Prototypes {{{ */
573 @interface NSObject (ProgressDelegate)
576 @implementation NSObject(ProgressDelegate)
578 - (void) _setProgressError:(NSArray *)args {
579 [self performSelector:@selector(setProgressError:forPackage:)
580 withObject:[args objectAtIndex:0]
581 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
587 @protocol ProgressDelegate
588 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
589 - (void) setProgressTitle:(NSString *)title;
590 - (void) setProgressPercent:(float)percent;
591 - (void) addProgressOutput:(NSString *)output;
594 @protocol ConfigurationDelegate
595 - (void) repairWithSelector:(SEL)selector;
596 - (void) setConfigurationData:(NSString *)data;
599 @protocol CydiaDelegate
600 - (void) installPackage:(Package *)package;
601 - (void) removePackage:(Package *)package;
602 - (void) slideUp:(UIAlertSheet *)alert;
603 - (void) distUpgrade;
606 - (void) askForSettings;
607 - (UIProgressHUD *) addProgressHUD;
611 /* Status Delegation {{{ */
613 public pkgAcquireStatus
616 _transient NSObject<ProgressDelegate> *delegate_;
624 void setDelegate(id delegate) {
625 delegate_ = delegate;
628 virtual bool MediaChange(std::string media, std::string drive) {
632 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
635 virtual void Fetch(pkgAcquire::ItemDesc &item) {
636 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
639 virtual void Done(pkgAcquire::ItemDesc &item) {
642 virtual void Fail(pkgAcquire::ItemDesc &item) {
644 item.Owner->Status == pkgAcquire::Item::StatIdle ||
645 item.Owner->Status == pkgAcquire::Item::StatDone
649 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
650 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
655 virtual bool Pulse(pkgAcquire *Owner) {
656 bool value = pkgAcquireStatus::Pulse(Owner);
659 double(CurrentBytes + CurrentItems) /
660 double(TotalBytes + TotalItems)
663 [delegate_ setProgressPercent:percent];
667 virtual void Start() {
670 virtual void Stop() {
674 /* Progress Delegation {{{ */
679 _transient id<ProgressDelegate> delegate_;
682 virtual void Update() {
683 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
684 [delegate_ setProgressPercent:(Percent / 100)];
693 void setDelegate(id delegate) {
694 delegate_ = delegate;
697 virtual void Done() {
698 [delegate_ setProgressPercent:1];
703 /* Database Interface {{{ */
704 @interface Database : NSObject {
706 pkgDepCache::Policy *policy_;
707 pkgRecords *records_;
708 pkgProblemResolver *resolver_;
709 pkgAcquire *fetcher_;
711 SPtr<pkgPackageManager> manager_;
712 pkgSourceList *list_;
714 NSMutableDictionary *sources_;
715 NSMutableArray *packages_;
717 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
726 - (void) _readCydia:(NSNumber *)fd;
727 - (void) _readStatus:(NSNumber *)fd;
728 - (void) _readOutput:(NSNumber *)fd;
732 - (Package *) packageWithName:(NSString *)name;
735 - (pkgCacheFile &) cache;
736 - (pkgDepCache::Policy *) policy;
737 - (pkgRecords *) records;
738 - (pkgProblemResolver *) resolver;
739 - (pkgAcquire &) fetcher;
740 - (NSArray *) packages;
741 - (NSArray *) sources;
750 - (void) updateWithStatus:(Status &)status;
752 - (void) setDelegate:(id)delegate;
753 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
757 /* Source Class {{{ */
758 @interface Source : NSObject {
759 NSString *description_;
764 NSString *distribution_;
768 NSString *defaultIcon_;
770 NSDictionary *record_;
774 - (Source *) initWithMetaIndex:(metaIndex *)index;
776 - (NSComparisonResult) compareByNameAndType:(Source *)source;
778 - (NSDictionary *) record;
782 - (NSString *) distribution;
788 - (NSString *) description;
789 - (NSString *) label;
790 - (NSString *) origin;
791 - (NSString *) version;
793 - (NSString *) defaultIcon;
797 @implementation Source
801 [distribution_ release];
804 if (description_ != nil)
805 [description_ release];
812 if (defaultIcon_ != nil)
813 [defaultIcon_ release];
820 - (Source *) initWithMetaIndex:(metaIndex *)index {
821 if ((self = [super init]) != nil) {
822 trusted_ = index->IsTrusted();
824 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
825 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
826 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
834 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
835 if (dindex != NULL) {
836 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
838 while (std::getline(release, line)) {
839 std::string::size_type colon(line.find(':'));
840 if (colon == std::string::npos)
843 std::string name(line.substr(0, colon));
844 std::string value(line.substr(colon + 1));
845 while (!value.empty() && value[0] == ' ')
846 value = value.substr(1);
848 if (name == "Default-Icon")
849 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
850 else if (name == "Description")
851 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
852 else if (name == "Label")
853 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
854 else if (name == "Origin")
855 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
856 else if (name == "Version")
857 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
861 record_ = [Sources_ objectForKey:[self key]];
863 record_ = [record_ retain];
867 - (NSComparisonResult) compareByNameAndType:(Source *)source {
868 NSDictionary *lhr = [self record];
869 NSDictionary *rhr = [source record];
872 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
874 NSString *lhs = [self name];
875 NSString *rhs = [source name];
877 if ([lhs length] != 0 && [rhs length] != 0) {
878 unichar lhc = [lhs characterAtIndex:0];
879 unichar rhc = [rhs characterAtIndex:0];
881 if (isalpha(lhc) && !isalpha(rhc))
882 return NSOrderedAscending;
883 else if (!isalpha(lhc) && isalpha(rhc))
884 return NSOrderedDescending;
887 return [lhs caseInsensitiveCompare:rhs];
890 - (NSDictionary *) record {
902 - (NSString *) distribution {
903 return distribution_;
906 - (NSString *) type {
911 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
914 - (NSString *) host {
915 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
918 - (NSString *) name {
919 return origin_ == nil ? [self host] : origin_;
922 - (NSString *) description {
926 - (NSString *) label {
927 return label_ == nil ? [self host] : label_;
930 - (NSString *) origin {
934 - (NSString *) version {
938 - (NSString *) defaultIcon {
944 /* Relationship Class {{{ */
945 @interface Relationship : NSObject {
956 @implementation Relationship
964 - (NSString *) type {
972 - (NSString *) name {
979 /* Package Class {{{ */
980 NSString *Scour(const char *field, const char *begin, const char *end) {
981 size_t i(0), l(strlen(field));
984 const char *name = begin + i;
985 const char *colon = name + l;
986 const char *value = colon + 1;
991 memcmp(name, field, l) == 0
993 while (value != end && value[0] == ' ')
995 const char *line = std::find(value, end, '\n');
996 while (line != value && line[-1] == ' ')
999 return [NSString stringWithUTF8Bytes:value length:(line - value)];
1001 begin = std::find(begin, end, '\n');
1009 @interface Package : NSObject {
1010 pkgCache::PkgIterator iterator_;
1011 _transient Database *database_;
1012 pkgCache::VerIterator version_;
1013 pkgCache::VerFileIterator file_;
1019 NSString *installed_;
1031 NSArray *relationships_;
1034 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1035 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
1037 - (pkgCache::PkgIterator) iterator;
1039 - (NSString *) section;
1040 - (Address *) maintainer;
1042 - (NSString *) description;
1043 - (NSString *) index;
1047 - (NSString *) latest;
1048 - (NSString *) installed;
1051 - (BOOL) upgradableAndEssential:(BOOL)essential;
1057 - (BOOL) halfConfigured;
1058 - (BOOL) halfInstalled;
1060 - (NSString *) mode;
1063 - (NSString *) name;
1064 - (NSString *) tagline;
1065 - (NSString *) icon;
1066 - (NSString *) website;
1067 - (Address *) author;
1069 - (NSArray *) relationships;
1071 - (Source *) source;
1072 - (NSString *) role;
1074 - (BOOL) matches:(NSString *)text;
1076 - (bool) hasSupportingRole;
1077 - (BOOL) hasTag:(NSString *)tag;
1079 - (NSComparisonResult) compareByName:(Package *)package;
1080 - (NSComparisonResult) compareBySection:(Package *)package;
1081 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
1082 - (NSComparisonResult) compareForChanges:(Package *)package;
1087 - (NSNumber *) isVisiblySearchedForBy:(NSString *)search;
1088 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
1089 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1090 - (NSNumber *) isVisibleInSource:(Source *)source;
1094 @implementation Package
1101 if (installed_ != nil)
1102 [installed_ release];
1110 if (website_ != nil)
1112 if (sponsor_ != nil)
1121 if (relationships_ != nil)
1122 [relationships_ release];
1127 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1128 if ((self = [super init]) != nil) {
1129 iterator_ = iterator;
1130 database_ = database;
1132 version_ = [database_ policy]->GetCandidateVer(iterator_);
1133 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1135 if (!version_.end())
1136 file_ = version_.FileList();
1138 pkgCache &cache([database_ cache]);
1139 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1142 pkgCache::VerIterator current = iterator_.CurrentVer();
1143 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1145 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1148 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1150 const char *begin, *end;
1151 parser->GetRec(begin, end);
1153 name_ = Scour("Name", begin, end);
1155 name_ = [name_ retain];
1156 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1157 icon_ = Scour("Icon", begin, end);
1159 icon_ = [icon_ retain];
1160 website_ = Scour("Homepage", begin, end);
1161 if (website_ == nil)
1162 website_ = Scour("Website", begin, end);
1163 if (website_ != nil)
1164 website_ = [website_ retain];
1165 NSString *sponsor = Scour("Sponsor", begin, end);
1167 sponsor_ = [[Address addressWithString:sponsor] retain];
1168 NSString *author = Scour("Author", begin, end);
1170 author_ = [[Address addressWithString:author] retain];
1171 NSString *tags = Scour("Tag", begin, end);
1173 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1177 for (int i(0), e([tags_ count]); i != e; ++i) {
1178 NSString *tag = [tags_ objectAtIndex:i];
1179 if ([tag hasPrefix:@"role::"]) {
1180 role_ = [[tag substringFromIndex:6] retain];
1185 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1186 if (metadata == nil || [metadata count] == 0) {
1187 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1191 [Packages_ setObject:metadata forKey:id_];
1197 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1198 return [[[Package alloc]
1199 initWithIterator:iterator
1204 - (pkgCache::PkgIterator) iterator {
1208 - (NSString *) section {
1209 const char *section = iterator_.Section();
1210 if (section == NULL)
1213 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1216 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1217 if (NSString *rename = [value objectForKey:@"Rename"]) {
1222 return [name stringByReplacingCharacter:'_' withCharacter:' '];
1225 - (Address *) maintainer {
1228 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1229 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1233 return version_.end() ? 0 : version_->InstalledSize;
1236 - (NSString *) description {
1239 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1240 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1242 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1243 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1244 if ([lines count] < 2)
1247 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1248 for (size_t i(1); i != [lines count]; ++i) {
1249 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1250 [trimmed addObject:trim];
1253 return [trimmed componentsJoinedByString:@"\n"];
1256 - (NSString *) index {
1257 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1258 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1262 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1265 - (NSString *) latest {
1269 - (NSString *) installed {
1274 return !version_.end();
1277 - (BOOL) upgradableAndEssential:(BOOL)essential {
1278 pkgCache::VerIterator current = iterator_.CurrentVer();
1281 return essential && [self essential];
1283 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1284 return !candidate.end() && candidate != current;
1288 - (BOOL) essential {
1289 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1293 return [database_ cache][iterator_].InstBroken();
1297 NSString *section = [self section];
1298 return [self hasSupportingRole] && (section == nil || isSectionVisible(section));
1302 unsigned char current = iterator_->CurrentState;
1303 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1306 - (BOOL) halfConfigured {
1307 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1310 - (BOOL) halfInstalled {
1311 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1315 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1316 return state.Mode != pkgDepCache::ModeKeep;
1319 - (NSString *) mode {
1320 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1322 switch (state.Mode) {
1323 case pkgDepCache::ModeDelete:
1324 if ((state.iFlags & pkgDepCache::Purge) != 0)
1329 case pkgDepCache::ModeKeep:
1330 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1335 case pkgDepCache::ModeInstall:
1336 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1337 return @"Reinstall";
1338 else switch (state.Status) {
1340 return @"Downgrade";
1346 return @"New Install";
1359 - (NSString *) name {
1360 return name_ == nil ? id_ : name_;
1363 - (NSString *) tagline {
1367 - (NSString *) icon {
1371 - (NSString *) website {
1375 - (Address *) sponsor {
1379 - (Address *) author {
1383 - (NSArray *) relationships {
1384 return relationships_;
1387 - (Source *) source {
1389 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1396 - (NSString *) role {
1400 - (BOOL) matches:(NSString *)text {
1406 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1407 if (range.location != NSNotFound)
1410 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1411 if (range.location != NSNotFound)
1414 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1415 if (range.location != NSNotFound)
1421 - (bool) hasSupportingRole {
1424 if ([role_ isEqualToString:@"enduser"])
1426 if ([Role_ isEqualToString:@"User"])
1428 if ([role_ isEqualToString:@"hacker"])
1430 if ([Role_ isEqualToString:@"Hacker"])
1432 if ([role_ isEqualToString:@"developer"])
1434 if ([Role_ isEqualToString:@"Developer"])
1439 - (BOOL) hasTag:(NSString *)tag {
1440 return tags_ == nil ? NO : [tags_ containsObject:tag];
1443 - (NSComparisonResult) compareByName:(Package *)package {
1444 NSString *lhs = [self name];
1445 NSString *rhs = [package name];
1447 if ([lhs length] != 0 && [rhs length] != 0) {
1448 unichar lhc = [lhs characterAtIndex:0];
1449 unichar rhc = [rhs characterAtIndex:0];
1451 if (isalpha(lhc) && !isalpha(rhc))
1452 return NSOrderedAscending;
1453 else if (!isalpha(lhc) && isalpha(rhc))
1454 return NSOrderedDescending;
1457 return [lhs caseInsensitiveCompare:rhs];
1460 - (NSComparisonResult) compareBySection:(Package *)package {
1461 NSString *lhs = [self section];
1462 NSString *rhs = [package section];
1464 if (lhs == NULL && rhs != NULL)
1465 return NSOrderedAscending;
1466 else if (lhs != NULL && rhs == NULL)
1467 return NSOrderedDescending;
1468 else if (lhs != NULL && rhs != NULL) {
1469 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1470 if (result != NSOrderedSame)
1474 return NSOrderedSame;
1477 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1478 NSString *lhs = [self section];
1479 NSString *rhs = [package section];
1481 if (lhs == NULL && rhs != NULL)
1482 return NSOrderedAscending;
1483 else if (lhs != NULL && rhs == NULL)
1484 return NSOrderedDescending;
1485 else if (lhs != NULL && rhs != NULL) {
1486 NSComparisonResult result = [lhs compare:rhs];
1487 if (result != NSOrderedSame)
1491 return [self compareByName:package];
1494 - (NSComparisonResult) compareForChanges:(Package *)package {
1495 BOOL lhs = [self upgradableAndEssential:YES];
1496 BOOL rhs = [package upgradableAndEssential:YES];
1499 return lhs ? NSOrderedAscending : NSOrderedDescending;
1501 switch ([[self seen] compare:[package seen]]) {
1502 case NSOrderedAscending:
1503 return NSOrderedDescending;
1506 case NSOrderedDescending:
1507 return NSOrderedAscending;
1513 return [self compareByName:package];
1517 pkgProblemResolver *resolver = [database_ resolver];
1518 resolver->Clear(iterator_);
1519 resolver->Protect(iterator_);
1520 pkgCacheFile &cache([database_ cache]);
1521 cache->MarkInstall(iterator_, false);
1522 pkgDepCache::StateCache &state((*cache)[iterator_]);
1523 if (!state.Install())
1524 cache->SetReInstall(iterator_, true);
1528 pkgProblemResolver *resolver = [database_ resolver];
1529 resolver->Clear(iterator_);
1530 resolver->Protect(iterator_);
1531 resolver->Remove(iterator_);
1532 [database_ cache]->MarkDelete(iterator_, true);
1535 - (NSNumber *) isVisiblySearchedForBy:(NSString *)search {
1536 return [NSNumber numberWithBool:(
1537 [self valid] && [self visible] && [self matches:search]
1541 - (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
1542 return [NSNumber numberWithBool:(
1543 (![number boolValue] || [self visible]) && [self installed] != nil
1547 - (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
1548 NSString *section = [self section];
1550 return [NSNumber numberWithBool:(
1551 [self valid] && [self visible] &&
1552 [self installed] == nil && (
1554 section == nil && [name length] == 0 ||
1555 [name isEqualToString:section]
1560 - (NSNumber *) isVisibleInSource:(Source *)source {
1561 return [NSNumber numberWithBool:([self source] == source && [self visible])];
1566 /* Section Class {{{ */
1567 @interface Section : NSObject {
1573 - (NSComparisonResult) compareByName:(Section *)section;
1574 - (Section *) initWithName:(NSString *)name;
1575 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1576 - (NSString *) name;
1579 - (void) addToCount;
1583 @implementation Section
1590 - (NSComparisonResult) compareByName:(Section *)section {
1591 NSString *lhs = [self name];
1592 NSString *rhs = [section name];
1594 if ([lhs length] != 0 && [rhs length] != 0) {
1595 unichar lhc = [lhs characterAtIndex:0];
1596 unichar rhc = [rhs characterAtIndex:0];
1598 if (isalpha(lhc) && !isalpha(rhc))
1599 return NSOrderedAscending;
1600 else if (!isalpha(lhc) && isalpha(rhc))
1601 return NSOrderedDescending;
1604 return [lhs caseInsensitiveCompare:rhs];
1607 - (Section *) initWithName:(NSString *)name {
1608 return [self initWithName:name row:0];
1611 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1612 if ((self = [super init]) != nil) {
1613 name_ = [name retain];
1618 - (NSString *) name {
1630 - (void) addToCount {
1637 /* Database Implementation {{{ */
1638 @implementation Database
1645 - (void) _readCydia:(NSNumber *)fd {
1646 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1648 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1649 std::istream is(&ib);
1652 while (std::getline(is, line)) {
1653 const char *data(line.c_str());
1654 //size_t size = line.size();
1655 fprintf(stderr, "C:%s\n", data);
1662 - (void) _readStatus:(NSNumber *)fd {
1663 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1665 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1666 std::istream is(&ib);
1669 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1670 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1672 while (std::getline(is, line)) {
1673 const char *data(line.c_str());
1674 size_t size = line.size();
1675 fprintf(stderr, "S:%s\n", data);
1677 if (conffile_r(data, size)) {
1678 [delegate_ setConfigurationData:conffile_r[1]];
1679 } else if (strncmp(data, "status: ", 8) == 0) {
1680 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1681 [delegate_ setProgressTitle:string];
1682 } else if (pmstatus_r(data, size)) {
1683 std::string type([pmstatus_r[1] UTF8String]);
1684 NSString *id = pmstatus_r[2];
1686 float percent([pmstatus_r[3] floatValue]);
1687 [delegate_ setProgressPercent:(percent / 100)];
1689 NSString *string = pmstatus_r[4];
1691 if (type == "pmerror")
1692 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1693 withObject:[NSArray arrayWithObjects:string, id, nil]
1696 else if (type == "pmstatus")
1697 [delegate_ setProgressTitle:string];
1698 else if (type == "pmconffile")
1699 [delegate_ setConfigurationData:string];
1700 else _assert(false);
1701 } else _assert(false);
1708 - (void) _readOutput:(NSNumber *)fd {
1709 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1711 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1712 std::istream is(&ib);
1715 while (std::getline(is, line)) {
1716 fprintf(stderr, "O:%s\n", line.c_str());
1717 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1728 - (Package *) packageWithName:(NSString *)name {
1729 if (static_cast<pkgDepCache *>(cache_) == NULL)
1731 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1732 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1735 - (Database *) init {
1736 if ((self = [super init]) != nil) {
1743 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1744 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1748 _assert(pipe(fds) != -1);
1751 _config->Set("APT::Keep-Fds::", cydiafd_);
1752 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 0"] UTF8String], _not(int));
1755 detachNewThreadSelector:@selector(_readCydia:)
1757 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1760 _assert(pipe(fds) != -1);
1764 detachNewThreadSelector:@selector(_readStatus:)
1766 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1769 _assert(pipe(fds) != -1);
1770 _assert(dup2(fds[0], 0) != -1);
1771 _assert(close(fds[0]) != -1);
1773 input_ = fdopen(fds[1], "a");
1775 _assert(pipe(fds) != -1);
1776 _assert(dup2(fds[1], 1) != -1);
1777 _assert(close(fds[1]) != -1);
1780 detachNewThreadSelector:@selector(_readOutput:)
1782 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1787 - (pkgCacheFile &) cache {
1791 - (pkgDepCache::Policy *) policy {
1795 - (pkgRecords *) records {
1799 - (pkgProblemResolver *) resolver {
1803 - (pkgAcquire &) fetcher {
1807 - (NSArray *) packages {
1811 - (NSArray *) sources {
1812 return [sources_ allValues];
1815 - (void) reloadData {
1834 if (!cache_.Open(progress_, true)) {
1836 if (!_error->PopMessage(error))
1839 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1841 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1842 [delegate_ repairWithSelector:@selector(configure)];
1843 else if (error == "The package lists or status file could not be parsed or opened.")
1844 [delegate_ repairWithSelector:@selector(update)];
1845 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1846 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1847 // else if (error == "The list of sources could not be read.")
1848 else _assert(false);
1853 now_ = [[NSDate date] retain];
1855 policy_ = new pkgDepCache::Policy();
1856 records_ = new pkgRecords(cache_);
1857 resolver_ = new pkgProblemResolver(cache_);
1858 fetcher_ = new pkgAcquire(&status_);
1861 list_ = new pkgSourceList();
1862 _assert(list_->ReadMainList());
1864 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1865 _assert(pkgApplyStatus(cache_));
1867 if (cache_->BrokenCount() != 0) {
1868 _assert(pkgFixBroken(cache_));
1869 _assert(cache_->BrokenCount() == 0);
1870 _assert(pkgMinimizeUpgrade(cache_));
1873 [sources_ removeAllObjects];
1874 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1875 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1876 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1878 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1879 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1883 [packages_ removeAllObjects];
1884 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1885 if (Package *package = [Package packageWithIterator:iterator database:self])
1886 [packages_ addObject:package];
1888 [packages_ sortUsingSelector:@selector(compareByName:)];
1891 - (void) configure {
1892 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1893 system([dpkg UTF8String]);
1901 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1902 _assert(!_error->PendingError());
1905 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1908 public pkgArchiveCleaner
1911 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1916 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1918 while (_error->PopMessage(error))
1919 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1924 pkgRecords records(cache_);
1926 lock_ = new FileFd();
1927 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1928 _assert(!_error->PendingError());
1931 // XXX: explain this with an error message
1932 _assert(list.ReadMainList());
1934 manager_ = (_system->CreatePM(cache_));
1935 _assert(manager_->GetArchives(fetcher_, &list, &records));
1936 _assert(!_error->PendingError());
1940 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1942 _assert(list.ReadMainList());
1943 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1944 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1947 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
1952 bool failed = false;
1953 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
1954 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
1957 std::string uri = (*item)->DescURI();
1958 std::string error = (*item)->ErrorText;
1960 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
1963 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1964 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
1975 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1977 if (_error->PendingError()) {
1982 if (result == pkgPackageManager::Failed) {
1987 if (result != pkgPackageManager::Completed) {
1992 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1994 _assert(list.ReadMainList());
1995 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1996 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1999 if (![before isEqualToArray:after])
2004 _assert(pkgDistUpgrade(cache_));
2008 [self updateWithStatus:status_];
2011 - (void) updateWithStatus:(Status &)status {
2013 _assert(list.ReadMainList());
2016 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2017 _assert(!_error->PendingError());
2019 pkgAcquire fetcher(&status);
2020 _assert(list.GetIndexes(&fetcher));
2022 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2023 bool failed = false;
2024 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2025 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2026 (*item)->Finished();
2030 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2031 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2032 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
2035 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
2040 - (void) setDelegate:(id)delegate {
2041 delegate_ = delegate;
2042 status_.setDelegate(delegate);
2043 progress_.setDelegate(delegate);
2046 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2047 pkgIndexFile *index(NULL);
2048 list_->FindIndex(file, index);
2049 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
2055 /* Confirmation View {{{ */
2056 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
2057 if ([packages count] == 0)
2060 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
2061 [fields setObject:text forKey:key];
2063 CGColor blue(space_, 0, 0, 0.4, 1);
2064 [text setTextColor:blue];
2067 @protocol ConfirmationViewDelegate
2072 @interface ConfirmationView : UIView {
2073 Database *database_;
2075 UITransitionView *transition_;
2077 UINavigationBar *navbar_;
2078 UIPreferencesTable *table_;
2079 NSMutableDictionary *fields_;
2080 UIAlertSheet *essential_;
2085 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
2089 @implementation ConfirmationView
2092 [navbar_ setDelegate:nil];
2093 [transition_ setDelegate:nil];
2094 [table_ setDataSource:nil];
2096 [transition_ release];
2101 if (essential_ != nil)
2102 [essential_ release];
2107 [transition_ transition:7 toView:nil];
2111 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2112 if (from != nil && to == nil)
2113 [self removeFromSuperview];
2116 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2119 if (essential_ != nil)
2120 [essential_ popupAlertAnimated:YES];
2122 [delegate_ confirm];
2131 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2132 NSString *context = [sheet context];
2134 if ([context isEqualToString:@"remove"])
2140 [delegate_ confirm];
2145 else if ([context isEqualToString:@"unable"])
2151 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2155 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2157 case 0: return @"Statistics";
2158 case 1: return @"Modifications";
2160 default: _assert(false);
2164 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2167 case 1: return [fields_ count];
2169 default: _assert(false);
2173 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2174 if (group != 1 || row == -1)
2177 _assert(size_t(row) < [fields_ count]);
2178 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2182 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2183 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2184 [cell setShowSelection:NO];
2187 case 0: switch (row) {
2189 [cell setTitle:@"Downloading"];
2190 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2194 [cell setTitle:@"Resuming At"];
2195 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2199 double size([database_ cache]->UsrSize());
2202 [cell setTitle:@"Disk Freeing"];
2203 [cell setValue:SizeString(-size)];
2205 [cell setTitle:@"Disk Using"];
2206 [cell setValue:SizeString(size)];
2210 default: _assert(false);
2214 _assert(size_t(row) < [fields_ count]);
2215 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2216 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2219 default: _assert(false);
2225 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2226 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2227 database_ = database;
2228 delegate_ = delegate;
2230 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2231 [self addSubview:transition_];
2233 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2235 CGSize navsize = [UINavigationBar defaultSize];
2236 CGRect navrect = {{0, 0}, navsize};
2237 CGRect bounds = [overlay_ bounds];
2239 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2241 [navbar_ setBarStyle:1];
2242 [navbar_ setDelegate:self];
2244 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2245 [navbar_ pushNavigationItem:navitem];
2246 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2248 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2250 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2251 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2252 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2253 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2254 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2258 pkgCacheFile &cache([database_ cache]);
2259 NSArray *packages = [database_ packages];
2260 for (size_t i(0), e = [packages count]; i != e; ++i) {
2261 Package *package = [packages objectAtIndex:i];
2262 pkgCache::PkgIterator iterator = [package iterator];
2263 pkgDepCache::StateCache &state(cache[iterator]);
2265 NSString *name([package name]);
2267 if (state.NewInstall())
2268 [installing addObject:name];
2269 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2270 [reinstalling addObject:name];
2271 else if (state.Upgrade())
2272 [upgrading addObject:name];
2273 else if (state.Downgrade())
2274 [downgrading addObject:name];
2275 else if (state.Delete()) {
2276 if ([package essential])
2278 [removing addObject:name];
2284 else if (Advanced_ || true) {
2285 essential_ = [[UIAlertSheet alloc]
2286 initWithTitle:@"Removing Essentials"
2287 buttons:[NSArray arrayWithObjects:
2288 @"Cancel Operation (Safe)",
2289 @"Force Removal (Unsafe)",
2291 defaultButtonIndex:0
2297 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2299 [essential_ setBodyText:@"This operation involves the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. If you continue, you may not be able to use Cydia to repair any damage."];
2301 essential_ = [[UIAlertSheet alloc]
2302 initWithTitle:@"Unable to Comply"
2303 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2304 defaultButtonIndex:0
2309 [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode under to continue and force this operation you will need to be activate the Advanced mode under Settings."];
2312 AddTextView(fields_, installing, @"Installing");
2313 AddTextView(fields_, reinstalling, @"Reinstalling");
2314 AddTextView(fields_, upgrading, @"Upgrading");
2315 AddTextView(fields_, downgrading, @"Downgrading");
2316 AddTextView(fields_, removing, @"Removing");
2318 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2319 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2322 [table_ setReusesTableCells:YES];
2323 [table_ setDataSource:self];
2324 [table_ reloadData];
2326 [overlay_ addSubview:navbar_];
2327 [overlay_ addSubview:table_];
2329 [view addSubview:self];
2331 [transition_ setDelegate:self];
2333 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2334 [transition_ transition:0 toView:blank];
2335 [transition_ transition:3 toView:overlay_];
2342 /* Progress Data {{{ */
2343 @interface ProgressData : NSObject {
2349 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2356 @implementation ProgressData
2358 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2359 if ((self = [super init]) != nil) {
2360 selector_ = selector;
2380 /* Progress View {{{ */
2381 @interface ProgressView : UIView <
2382 ConfigurationDelegate,
2385 _transient Database *database_;
2387 UIView *background_;
2388 UITransitionView *transition_;
2390 UINavigationBar *navbar_;
2391 UIProgressBar *progress_;
2392 UITextView *output_;
2393 UITextLabel *status_;
2394 UIPushButton *close_;
2399 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2401 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2402 - (void) setContentView:(UIView *)view;
2405 - (void) _retachThread;
2406 - (void) _detachNewThreadData:(ProgressData *)data;
2407 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2413 @protocol ProgressViewDelegate
2414 - (void) progressViewIsComplete:(ProgressView *)sender;
2417 @implementation ProgressView
2420 [transition_ setDelegate:nil];
2421 [navbar_ setDelegate:nil];
2424 if (background_ != nil)
2425 [background_ release];
2426 [transition_ release];
2429 [progress_ release];
2436 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2437 if (bootstrap_ && from == overlay_ && to == view_)
2441 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2442 if ((self = [super initWithFrame:frame]) != nil) {
2443 database_ = database;
2444 delegate_ = delegate;
2446 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2447 [transition_ setDelegate:self];
2449 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2452 [overlay_ setBackgroundColor:Black_];
2454 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2455 [background_ setBackgroundColor:Black_];
2456 [self addSubview:background_];
2459 [self addSubview:transition_];
2461 CGSize navsize = [UINavigationBar defaultSize];
2462 CGRect navrect = {{0, 0}, navsize};
2464 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2465 [overlay_ addSubview:navbar_];
2467 [navbar_ setBarStyle:1];
2468 [navbar_ setDelegate:self];
2470 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2471 [navbar_ pushNavigationItem:navitem];
2473 CGRect bounds = [overlay_ bounds];
2474 CGSize prgsize = [UIProgressBar defaultSize];
2477 (bounds.size.width - prgsize.width) / 2,
2478 bounds.size.height - prgsize.height - 20
2481 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2482 [progress_ setStyle:0];
2484 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2486 bounds.size.height - prgsize.height - 50,
2487 bounds.size.width - 20,
2491 [status_ setColor:White_];
2492 [status_ setBackgroundColor:Clear_];
2494 [status_ setCentersHorizontally:YES];
2495 //[status_ setFont:font];
2497 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2499 navrect.size.height + 20,
2500 bounds.size.width - 20,
2501 bounds.size.height - navsize.height - 62 - navrect.size.height
2504 //[output_ setTextFont:@"Courier New"];
2505 [output_ setTextSize:12];
2507 [output_ setTextColor:White_];
2508 [output_ setBackgroundColor:Clear_];
2510 [output_ setMarginTop:0];
2511 [output_ setAllowsRubberBanding:YES];
2512 [output_ setEditable:NO];
2514 [overlay_ addSubview:output_];
2516 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2518 bounds.size.height - prgsize.height - 50,
2519 bounds.size.width - 20,
2523 [close_ setAutosizesToFit:NO];
2524 [close_ setDrawsShadow:YES];
2525 [close_ setStretchBackground:YES];
2526 [close_ setTitle:@"Return to Cydia"];
2527 [close_ setEnabled:YES];
2529 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2530 [close_ setTitleFont:bold];
2533 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2534 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2535 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2539 - (void) setContentView:(UIView *)view {
2540 view_ = [view retain];
2543 - (void) resetView {
2544 [transition_ transition:6 toView:view_];
2547 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2548 NSString *context = [sheet context];
2549 if ([context isEqualToString:@"conffile"]) {
2550 FILE *input = [database_ input];
2554 fprintf(input, "N\n");
2558 fprintf(input, "Y\n");
2569 - (void) closeButtonPushed {
2570 [delegate_ progressViewIsComplete:self];
2574 - (void) _retachThread {
2575 UINavigationItem *item = [navbar_ topItem];
2576 [item setTitle:@"Complete"];
2578 [overlay_ addSubview:close_];
2579 [progress_ removeFromSuperview];
2580 [status_ removeFromSuperview];
2583 notify_post("com.apple.mobile.application_installed");
2586 [delegate_ setStatusBarShowsProgress:NO];
2591 - (void) _detachNewThreadData:(ProgressData *)data {
2592 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2594 [[data target] performSelector:[data selector] withObject:[data object]];
2597 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2602 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2603 UINavigationItem *item = [navbar_ topItem];
2604 [item setTitle:title];
2606 [status_ setText:nil];
2607 [output_ setText:@""];
2608 [progress_ setProgress:0];
2610 [close_ removeFromSuperview];
2611 [overlay_ addSubview:progress_];
2612 [overlay_ addSubview:status_];
2614 [delegate_ setStatusBarShowsProgress:YES];
2617 [transition_ transition:6 toView:overlay_];
2620 detachNewThreadSelector:@selector(_detachNewThreadData:)
2622 withObject:[[ProgressData alloc]
2623 initWithSelector:selector
2630 - (void) repairWithSelector:(SEL)selector {
2632 detachNewThreadSelector:selector
2635 title:@"Repairing..."
2639 - (void) setConfigurationData:(NSString *)data {
2641 performSelectorOnMainThread:@selector(_setConfigurationData:)
2647 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2648 Package *package = id == nil ? nil : [database_ packageWithName:id];
2650 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2651 initWithTitle:(package == nil ? @"Source Error" : [package name])
2652 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2653 defaultButtonIndex:0
2658 [sheet setBodyText:error];
2659 [sheet popupAlertAnimated:YES];
2662 - (void) setProgressTitle:(NSString *)title {
2664 performSelectorOnMainThread:@selector(_setProgressTitle:)
2670 - (void) setProgressPercent:(float)percent {
2672 performSelectorOnMainThread:@selector(_setProgressPercent:)
2673 withObject:[NSNumber numberWithFloat:percent]
2678 - (void) addProgressOutput:(NSString *)output {
2680 performSelectorOnMainThread:@selector(_addProgressOutput:)
2686 - (void) _setConfigurationData:(NSString *)data {
2687 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2689 _assert(conffile_r(data));
2691 NSString *ofile = conffile_r[1];
2692 //NSString *nfile = conffile_r[2];
2694 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2695 initWithTitle:@"Configuration Upgrade"
2696 buttons:[NSArray arrayWithObjects:
2697 @"Keep My Old Copy",
2698 @"Accept The New Copy",
2699 // XXX: @"See What Changed",
2701 defaultButtonIndex:0
2706 [sheet setBodyText:[NSString stringWithFormat:
2707 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2710 [sheet popupAlertAnimated:YES];
2713 - (void) _setProgressTitle:(NSString *)title {
2714 [status_ setText:[title stringByAppendingString:@"..."]];
2717 - (void) _setProgressPercent:(NSNumber *)percent {
2718 [progress_ setProgress:[percent floatValue]];
2721 - (void) _addProgressOutput:(NSString *)output {
2722 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2723 CGSize size = [output_ contentSize];
2724 CGRect rect = {{0, size.height}, {size.width, 0}};
2725 [output_ scrollRectToVisible:rect animated:YES];
2728 - (BOOL) isRunning {
2735 /* Package Cell {{{ */
2736 @interface PackageCell : UISimpleTableCell {
2739 NSString *description_;
2741 //UIImageView *trusted_;
2743 UIImageView *badge_;
2744 UITextLabel *status_;
2748 - (PackageCell *) init;
2749 - (void) setPackage:(Package *)package;
2751 + (int) heightForPackage:(Package *)package;
2755 @implementation PackageCell
2757 - (void) clearPackage {
2768 if (description_ != nil) {
2769 [description_ release];
2773 if (source_ != nil) {
2780 [self clearPackage];
2785 //[trusted_ release];
2789 - (PackageCell *) init {
2790 if ((self = [super init]) != nil) {
2792 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2794 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2795 [status_ setBackgroundColor:Clear_];
2796 [status_ setFont:small];
2801 - (void) setPackage:(Package *)package {
2802 [self clearPackage];
2804 Source *source = [package source];
2807 if (NSString *icon = [package icon])
2808 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];
2809 if (icon_ == nil) if (NSString *section = [package section])
2810 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sections/%@.png", Simplify(section)]];
2811 /*if (icon_ == nil) if (NSString *icon = [source defaultIcon])
2812 icon_ = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2814 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
2816 icon_ = [icon_ retain];
2818 name_ = [[package name] retain];
2819 description_ = [[package tagline] retain];
2821 NSString *label = nil;
2822 bool trusted = false;
2824 if (source != nil) {
2825 label = [source label];
2826 trusted = [source trusted];
2827 } else if ([[package id] isEqualToString:@"firmware"])
2830 label = @"Unknown/Local";
2832 NSString *from = [NSString stringWithFormat:@"from %@", label];
2834 NSString *section = Simplify([package section]);
2835 if (section != nil && ![section isEqualToString:label])
2836 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2838 source_ = [from retain];
2841 [badge_ removeFromSuperview];
2842 [status_ removeFromSuperview];
2844 if (NSString *mode = [package mode]) {
2845 [badge_ setImage:[UIImage applicationImageNamed:
2846 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2849 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2850 [status_ setColor:Blueish_];
2851 } else if ([package half]) {
2852 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2853 [status_ setText:@"Package Damaged"];
2854 [status_ setColor:Red_];
2856 [badge_ setImage:nil];
2857 [status_ setText:nil];
2861 [self addSubview:badge_];
2862 [self addSubview:status_];
2867 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
2869 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
2876 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
2877 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
2881 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
2883 [super drawContentInRect:rect selected:selected];
2886 + (int) heightForPackage:(Package *)package {
2887 NSString *tagline([package tagline]);
2888 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
2890 if ([package hasMode] || [package half])
2899 /* Section Cell {{{ */
2900 @interface SectionCell : UISimpleTableCell {
2905 UISwitchControl *switch_;
2910 - (void) setSection:(Section *)section editing:(BOOL)editing;
2914 @implementation SectionCell
2916 - (void) clearSection {
2917 if (section_ != nil) {
2927 if (count_ != nil) {
2934 [self clearSection];
2941 if ((self = [super init]) != nil) {
2942 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
2944 switch_ = [[UISwitchControl alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
2945 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
2949 - (void) onSwitch:(id)sender {
2950 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
2951 if (metadata == nil) {
2952 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
2953 [Sections_ setObject:metadata forKey:section_];
2957 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
2960 - (void) setSection:(Section *)section editing:(BOOL)editing {
2961 if (editing != editing_) {
2963 [switch_ removeFromSuperview];
2965 [self addSubview:switch_];
2969 [self clearSection];
2971 if (section == nil) {
2972 name_ = [@"All Packages" retain];
2975 section_ = [section name];
2976 if (section_ != nil)
2977 section_ = [section_ retain];
2978 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
2979 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
2982 [switch_ setValue:isSectionVisible(section_) animated:NO];
2986 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
2987 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
2994 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
2996 CGSize size = [count_ sizeWithFont:Font14_];
3000 [count_ drawAtPoint:CGPointMake(12 + (29 - size.width) / 2, 15) withFont:Font12Bold_];
3002 [super drawContentInRect:rect selected:selected];
3008 /* File Table {{{ */
3009 @interface FileTable : RVPage {
3010 _transient Database *database_;
3013 NSMutableArray *files_;
3017 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3018 - (void) setPackage:(Package *)package;
3022 @implementation FileTable
3025 if (package_ != nil)
3034 - (int) numberOfRowsInTable:(UITable *)table {
3035 return files_ == nil ? 0 : [files_ count];
3038 - (float) table:(UITable *)table heightForRow:(int)row {
3042 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3043 if (reusing == nil) {
3044 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
3045 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3046 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
3049 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3053 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3057 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3058 if ((self = [super initWithBook:book]) != nil) {
3059 database_ = database;
3061 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3063 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3064 [self addSubview:list_];
3066 UITableColumn *column = [[[UITableColumn alloc]
3067 initWithTitle:@"Name"
3069 width:[self frame].size.width
3072 [list_ setDataSource:self];
3073 [list_ setSeparatorStyle:1];
3074 [list_ addTableColumn:column];
3075 [list_ setDelegate:self];
3076 [list_ setReusesTableCells:YES];
3080 - (void) setPackage:(Package *)package {
3081 if (package_ != nil) {
3082 [package_ autorelease];
3091 [files_ removeAllObjects];
3093 if (package != nil) {
3094 package_ = [package retain];
3095 name_ = [[package id] retain];
3097 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3100 std::ifstream fin([path UTF8String]);
3102 while (std::getline(fin, line))
3103 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3106 if ([files_ count] != 0) {
3107 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3108 [files_ removeObjectAtIndex:0];
3109 [files_ sortUsingSelector:@selector(compareByPath:)];
3111 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3112 [stack addObject:@"/"];
3114 for (int i(0), e([files_ count]); i != e; ++i) {
3115 NSString *file = [files_ objectAtIndex:i];
3116 while (![file hasPrefix:[stack lastObject]])
3117 [stack removeLastObject];
3118 NSString *directory = [stack lastObject];
3119 [stack addObject:[file stringByAppendingString:@"/"]];
3120 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3121 ([stack count] - 2) * 3, "",
3122 [file substringFromIndex:[directory length]]
3131 - (void) resetViewAnimated:(BOOL)animated {
3132 [list_ resetViewAnimated:animated];
3135 - (void) reloadData {
3136 [self setPackage:[database_ packageWithName:name_]];
3137 [self reloadButtons];
3140 - (NSString *) title {
3141 return @"Installed Files";
3144 - (NSString *) backButtonTitle {
3150 /* Package View {{{ */
3151 @interface PackageView : RVPage {
3152 _transient Database *database_;
3153 UIPreferencesTable *table_;
3156 UITextView *description_;
3157 NSMutableArray *buttons_;
3160 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3161 - (void) setPackage:(Package *)package;
3165 @implementation PackageView
3168 [table_ setDataSource:nil];
3169 [table_ setDelegate:nil];
3171 if (package_ != nil)
3175 if (description_ != nil)
3176 [description_ release];
3182 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
3184 if ([package_ installed] != nil)
3186 if ([package_ source] != nil)
3191 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
3194 else if ([package_ installed] != nil && group-- == 0)
3195 return @"Installed Package";
3196 else if (group-- == 0)
3197 return @"Package Details";
3198 else if ([package_ source] != nil && group-- == 0)
3199 return @"Source Information";
3200 else _assert(false);
3203 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
3204 if (description_ == nil || group != 0 || row != ([package_ author] == nil ? 1 : 2))
3207 return [description_ visibleTextRect].size.height + TextViewOffset_;
3210 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
3213 if ([package_ author] != nil)
3215 if (description_ != nil)
3217 if ([package_ website] != nil)
3220 } else if ([package_ installed] != nil && group-- == 0)
3222 else if (group-- == 0) {
3224 if ([package_ size] != 0)
3226 if ([package_ maintainer] != nil)
3228 if ([package_ sponsor] != nil)
3230 if ([package_ relationships] != nil)
3232 if ([[package_ source] trusted])
3235 } else if ([package_ source] != nil && group-- == 0) {
3236 Source *source = [package_ source];
3237 NSString *description = [source description];
3239 if (description != nil && ![description isEqualToString:[source label]])
3241 if ([source origin] != nil)
3244 } else _assert(false);
3247 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3248 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3249 [cell setShowSelection:NO];
3253 } else if (row-- == 0) {
3254 [cell setTitle:[package_ name]];
3255 [cell setValue:[package_ latest]];
3256 } else if ([package_ author] != nil && row-- == 0) {
3257 [cell setTitle:@"Author"];
3258 [cell setValue:[[package_ author] name]];
3259 [cell setShowDisclosure:YES];
3260 [cell setShowSelection:YES];
3261 } else if (description_ != nil && row-- == 0) {
3262 [cell addSubview:description_];
3263 } else if ([package_ website] != nil && row-- == 0) {
3264 [cell setTitle:@"More Information"];
3265 [cell setShowDisclosure:YES];
3266 [cell setShowSelection:YES];
3267 } else _assert(false);
3268 } else if ([package_ installed] != nil && group-- == 0) {
3270 } else if (row-- == 0) {
3271 [cell setTitle:@"Version"];
3272 NSString *installed([package_ installed]);
3273 [cell setValue:(installed == nil ? @"n/a" : installed)];
3274 } else if (row-- == 0) {
3275 [cell setTitle:@"Filesystem Content"];
3276 [cell setShowDisclosure:YES];
3277 [cell setShowSelection:YES];
3278 } else _assert(false);
3279 } else if (group-- == 0) {
3281 } else if (row-- == 0) {
3282 [cell setTitle:@"Identifier"];
3283 [cell setValue:[package_ id]];
3284 } else if (row-- == 0) {
3285 [cell setTitle:@"Section"];
3286 NSString *section([package_ section]);
3287 [cell setValue:(section == nil ? @"n/a" : section)];
3288 } else if ([package_ size] != 0 && row-- == 0) {
3289 [cell setTitle:@"Expanded Size"];
3290 [cell setValue:SizeString([package_ size])];
3291 } else if ([package_ maintainer] != nil && row-- == 0) {
3292 [cell setTitle:@"Maintainer"];
3293 [cell setValue:[[package_ maintainer] name]];
3294 [cell setShowDisclosure:YES];
3295 [cell setShowSelection:YES];
3296 } else if ([package_ sponsor] != nil && row-- == 0) {
3297 [cell setTitle:@"Sponsor"];
3298 [cell setValue:[[package_ sponsor] name]];
3299 [cell setShowDisclosure:YES];
3300 [cell setShowSelection:YES];
3301 } else if ([package_ relationships] != nil && row-- == 0) {
3302 [cell setTitle:@"Package Relationships"];
3303 [cell setShowDisclosure:YES];
3304 [cell setShowSelection:YES];
3305 } else if ([[package_ source] trusted] && row-- == 0) {
3306 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3307 [cell setValue:@"This package has been signed."];
3308 } else _assert(false);
3309 } else if ([package_ source] != nil && group-- == 0) {
3310 Source *source = [package_ source];
3311 NSString *description = [source description];
3314 } else if (row-- == 0) {
3315 NSString *label = [source label];
3317 label = [source uri];
3318 [cell setTitle:label];
3319 [cell setValue:[source version]];
3320 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3321 [cell setValue:description];
3322 } else if ([source origin] != nil && row-- == 0) {
3323 [cell setTitle:@"Origin"];
3324 [cell setValue:[source origin]];
3325 } else _assert(false);
3326 } else _assert(false);
3331 - (BOOL) canSelectRow:(int)row {
3335 - (void) tableRowSelected:(NSNotification *)notification {
3336 int row = [table_ selectedRow];
3340 #define _else else goto _label; return; } _label:
3344 } else if (row-- == 0) {
3345 } else if ([package_ author] != nil && row-- == 0) {
3346 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3347 [[package_ author] email],
3348 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3350 ] stringByAddingPercentEscapes]
3352 } else if (description_ != nil && row-- == 0) {
3353 } else if ([package_ website] != nil && row-- == 0) {
3354 NSURL *url = [NSURL URLWithString:[package_ website]];
3355 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3356 [browser setDelegate:delegate_];
3357 [book_ pushPage:browser];
3358 [browser loadURL:url];
3359 } _else if ([package_ installed] != nil) {
3361 } else if (row-- == 0) {
3362 } else if (row-- == 0) {
3363 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3364 [files setDelegate:delegate_];
3365 [files setPackage:package_];
3366 [book_ pushPage:files];
3369 } else if (row-- == 0) {
3370 } else if (row-- == 0) {
3371 } else if ([package_ size] != 0 && row-- == 0) {
3372 } else if ([package_ maintainer] != nil && row-- == 0) {
3373 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3374 [[package_ maintainer] email],
3375 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3377 ] stringByAddingPercentEscapes]
3379 } else if ([package_ sponsor] != nil && row-- == 0) {
3380 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3381 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3382 [browser setDelegate:delegate_];
3383 [book_ pushPage:browser];
3384 [browser loadURL:url];
3385 } else if ([package_ relationships] != nil && row-- == 0) {
3386 } else if ([[package_ source] trusted] && row-- == 0) {
3387 } _else if ([package_ source] != nil) {
3388 Source *source = [package_ source];
3389 NSString *description = [source description];
3392 } else if (row-- == 0) {
3393 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3394 } else if ([source origin] != nil && row-- == 0) {
3395 } _else _assert(false);
3400 - (void) _clickButtonWithName:(NSString *)name {
3401 if ([name isEqualToString:@"Install"])
3402 [delegate_ installPackage:package_];
3403 else if ([name isEqualToString:@"Reinstall"])
3404 [delegate_ installPackage:package_];
3405 else if ([name isEqualToString:@"Remove"])
3406 [delegate_ removePackage:package_];
3407 else if ([name isEqualToString:@"Upgrade"])
3408 [delegate_ installPackage:package_];
3409 else _assert(false);
3412 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3413 int count = [buttons_ count];
3414 _assert(count != 0);
3415 _assert(button <= count + 1);
3417 if (count != button - 1)
3418 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3423 - (void) _rightButtonClicked {
3424 int count = [buttons_ count];
3425 _assert(count != 0);
3428 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3430 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3431 [buttons addObjectsFromArray:buttons_];
3432 [buttons addObject:@"Cancel"];
3434 [delegate_ slideUp:[[[UIAlertSheet alloc]
3437 defaultButtonIndex:2
3444 - (NSString *) rightButtonTitle {
3445 int count = [buttons_ count];
3446 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3449 - (NSString *) title {
3453 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3454 if ((self = [super initWithBook:book]) != nil) {
3455 database_ = database;
3457 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3458 [self addSubview:table_];
3460 [table_ setDataSource:self];
3461 [table_ setDelegate:self];
3463 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3467 - (void) setPackage:(Package *)package {
3468 if (package_ != nil) {
3469 [package_ autorelease];
3478 if (description_ != nil) {
3479 [description_ release];
3483 [buttons_ removeAllObjects];
3485 if (package != nil) {
3486 package_ = [package retain];
3487 name_ = [[package id] retain];
3489 NSString *description([package description]);
3490 if (description == nil)
3491 description = [package tagline];
3492 if (description != nil) {
3493 description_ = [GetTextView(description, 12, true) retain];
3494 [description_ setTextColor:Black_];
3497 [table_ reloadData];
3499 if ([package_ source] == nil);
3500 else if ([package_ upgradableAndEssential:NO])
3501 [buttons_ addObject:@"Upgrade"];
3502 else if ([package_ installed] == nil)
3503 [buttons_ addObject:@"Install"];
3505 [buttons_ addObject:@"Reinstall"];
3506 if ([package_ installed] != nil)
3507 [buttons_ addObject:@"Remove"];
3511 - (void) resetViewAnimated:(BOOL)animated {
3512 [table_ resetViewAnimated:animated];
3515 - (void) reloadData {
3516 [self setPackage:[database_ packageWithName:name_]];
3517 [self reloadButtons];
3522 /* Package Table {{{ */
3523 @interface PackageTable : RVPage {
3524 _transient Database *database_;
3528 NSMutableArray *packages_;
3529 NSMutableArray *sections_;
3530 UISectionList *list_;
3533 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3535 - (void) setDelegate:(id)delegate;
3536 - (void) setObject:(id)object;
3538 - (void) reloadData;
3539 - (void) resetCursor;
3541 - (UISectionList *) list;
3543 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3547 @implementation PackageTable
3550 [list_ setDataSource:nil];
3555 [packages_ release];
3556 [sections_ release];
3561 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3562 return [sections_ count];
3565 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3566 return [[sections_ objectAtIndex:section] name];
3569 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3570 return [[sections_ objectAtIndex:section] row];
3573 - (int) numberOfRowsInTable:(UITable *)table {
3574 return [packages_ count];
3577 - (float) table:(UITable *)table heightForRow:(int)row {
3578 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3581 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3583 reusing = [[[PackageCell alloc] init] autorelease];
3584 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3588 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3592 - (void) tableRowSelected:(NSNotification *)notification {
3593 int row = [[notification object] selectedRow];
3597 Package *package = [packages_ objectAtIndex:row];
3598 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3599 [view setDelegate:delegate_];
3600 [view setPackage:package];
3601 [book_ pushPage:view];
3604 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3605 if ((self = [super initWithBook:book]) != nil) {
3606 database_ = database;
3607 title_ = [title retain];
3609 object_ = object == nil ? nil : [object retain];
3611 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3612 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3614 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3615 [list_ setDataSource:self];
3617 UITableColumn *column = [[[UITableColumn alloc]
3618 initWithTitle:@"Name"
3620 width:[self frame].size.width
3623 UITable *table = [list_ table];
3624 [table setSeparatorStyle:1];
3625 [table addTableColumn:column];
3626 [table setDelegate:self];
3627 [table setReusesTableCells:YES];
3629 [self addSubview:list_];
3634 - (void) setDelegate:(id)delegate {
3635 delegate_ = delegate;
3638 - (void) setObject:(id)object {
3644 object_ = [object retain];
3647 - (void) reloadData {
3648 NSArray *packages = [database_ packages];
3650 [packages_ removeAllObjects];
3651 [sections_ removeAllObjects];
3653 for (size_t i(0); i != [packages count]; ++i) {
3654 Package *package([packages objectAtIndex:i]);
3655 if ([[package performSelector:filter_ withObject:object_] boolValue])
3656 [packages_ addObject:package];
3659 Section *section = nil;
3661 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3662 Package *package = [packages_ objectAtIndex:offset];
3663 NSString *name = [package index];
3665 if (section == nil || ![[section name] isEqualToString:name]) {
3666 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3667 [sections_ addObject:section];
3670 [section addToCount];
3676 - (NSString *) title {
3680 - (void) resetViewAnimated:(BOOL)animated {
3681 [list_ resetViewAnimated:animated];
3684 - (void) resetCursor {
3685 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3688 - (UISectionList *) list {
3692 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3693 [list_ setShouldHideHeaderInShortLists:hide];
3699 /* Add Source View {{{ */
3700 @interface AddSourceView : RVPage {
3701 _transient Database *database_;
3704 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3708 @implementation AddSourceView
3710 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3711 if ((self = [super initWithBook:book]) != nil) {
3712 database_ = database;
3718 /* Source Cell {{{ */
3719 @interface SourceCell : UITableCell {
3722 NSString *description_;
3728 - (SourceCell *) initWithSource:(Source *)source;
3732 @implementation SourceCell
3737 [description_ release];
3742 - (SourceCell *) initWithSource:(Source *)source {
3743 if ((self = [super init]) != nil) {
3745 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3747 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3748 icon_ = [icon_ retain];
3750 origin_ = [[source name] retain];
3751 label_ = [[source uri] retain];
3752 description_ = [[source description] retain];
3756 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3758 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3765 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3769 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3773 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3775 [super drawContentInRect:rect selected:selected];
3780 /* Source Table {{{ */
3781 @interface SourceTable : RVPage {
3782 _transient Database *database_;
3783 UISectionList *list_;
3784 NSMutableArray *sources_;
3785 UIAlertSheet *alert_;
3789 UIProgressHUD *hud_;
3792 //NSURLConnection *installer_;
3793 NSURLConnection *trivial_bz2_;
3794 NSURLConnection *trivial_gz_;
3795 //NSURLConnection *automatic_;
3800 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3804 @implementation SourceTable
3806 - (void) _deallocConnection:(NSURLConnection *)connection {
3807 if (connection != nil) {
3808 [connection cancel];
3809 //[connection setDelegate:nil];
3810 [connection release];
3815 [[list_ table] setDelegate:nil];
3816 [list_ setDataSource:nil];
3825 //[self _deallocConnection:installer_];
3826 [self _deallocConnection:trivial_gz_];
3827 [self _deallocConnection:trivial_bz2_];
3828 //[self _deallocConnection:automatic_];
3835 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3836 return offset_ == 0 ? 1 : 2;
3839 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3840 switch (section + (offset_ == 0 ? 1 : 0)) {
3841 case 0: return @"Entered by User";
3842 case 1: return @"Installed by Packages";
3850 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3851 switch (section + (offset_ == 0 ? 1 : 0)) {
3853 case 1: return offset_;
3861 - (int) numberOfRowsInTable:(UITable *)table {
3862 return [sources_ count];
3865 - (float) table:(UITable *)table heightForRow:(int)row {
3866 Source *source = [sources_ objectAtIndex:row];
3867 return [source description] == nil ? 56 : 73;
3870 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3871 Source *source = [sources_ objectAtIndex:row];
3872 // XXX: weird warning, stupid selectors ;P
3873 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3876 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3880 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3884 - (void) tableRowSelected:(NSNotification*)notification {
3885 UITable *table([list_ table]);
3886 int row([table selectedRow]);
3890 Source *source = [sources_ objectAtIndex:row];
3892 PackageTable *packages = [[[PackageTable alloc]
3895 title:[source label]
3896 filter:@selector(isVisibleInSource:)
3900 [packages setDelegate:delegate_];
3902 [book_ pushPage:packages];
3905 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3906 Source *source = [sources_ objectAtIndex:row];
3907 return [source record] != nil;
3910 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3911 [[list_ table] setDeleteConfirmationRow:row];
3914 - (void) table:(UITable *)table deleteRow:(int)row {
3915 Source *source = [sources_ objectAtIndex:row];
3916 [Sources_ removeObjectForKey:[source key]];
3917 [delegate_ syncData];
3920 - (void) _endConnection:(NSURLConnection *)connection {
3921 NSURLConnection **field = NULL;
3922 if (connection == trivial_bz2_)
3923 field = &trivial_bz2_;
3924 else if (connection == trivial_gz_)
3925 field = &trivial_gz_;
3926 _assert(field != NULL);
3927 [connection release];
3931 trivial_bz2_ == nil &&
3934 [delegate_ setStatusBarShowsProgress:NO];
3937 [hud_ removeFromSuperview];
3942 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3945 @"./", @"Distribution",
3946 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3948 [delegate_ syncData];
3949 } else if (error_ != nil) {
3950 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3951 initWithTitle:@"Verification Error"
3952 buttons:[NSArray arrayWithObjects:@"OK", nil]
3953 defaultButtonIndex:0
3958 [sheet setBodyText:[error_ localizedDescription]];
3959 [sheet popupAlertAnimated:YES];
3961 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3962 initWithTitle:@"Did not Find Repository"
3963 buttons:[NSArray arrayWithObjects:@"OK", nil]
3964 defaultButtonIndex:0
3969 [sheet setBodyText:@"The indicated repository could not be found. This could be because you are trying to add a legacy Installer repository (these are not supported). Also, this interface is only capable of working with exact repository URLs. If you host a repository and are having issues please contact the author of Cydia with any questions you have."];
3970 [sheet popupAlertAnimated:YES];
3976 if (error_ != nil) {
3983 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3984 switch ([response statusCode]) {
3990 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3991 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3993 error_ = [error retain];
3994 [self _endConnection:connection];
3997 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
3998 [self _endConnection:connection];
4001 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4002 NSMutableURLRequest *request = [NSMutableURLRequest
4003 requestWithURL:[NSURL URLWithString:href]
4004 cachePolicy:NSURLRequestUseProtocolCachePolicy
4005 timeoutInterval:20.0
4008 [request setHTTPMethod:method];
4010 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4013 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4014 NSString *context = [sheet context];
4015 if ([context isEqualToString:@"source"])
4018 NSString *href = [[sheet textField] text];
4020 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4022 if (![href hasSuffix:@"/"])
4023 href_ = [href stringByAppendingString:@"/"];
4026 href_ = [href_ retain];
4028 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4029 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4030 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4034 hud_ = [delegate_ addProgressHUD];
4035 [hud_ setText:@"Verifying URL"];
4048 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4049 if ((self = [super initWithBook:book]) != nil) {
4050 database_ = database;
4051 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4053 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4054 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4055 [list_ setShouldHideHeaderInShortLists:NO];
4057 [self addSubview:list_];
4058 [list_ setDataSource:self];
4060 UITableColumn *column = [[UITableColumn alloc]
4061 initWithTitle:@"Name"
4063 width:[self frame].size.width
4066 UITable *table = [list_ table];
4067 [table setSeparatorStyle:1];
4068 [table addTableColumn:column];
4069 [table setDelegate:self];
4075 - (void) reloadData {
4077 _assert(list.ReadMainList());
4079 [sources_ removeAllObjects];
4080 [sources_ addObjectsFromArray:[database_ sources]];
4081 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4083 int count = [sources_ count];
4084 for (offset_ = 0; offset_ != count; ++offset_) {
4085 Source *source = [sources_ objectAtIndex:offset_];
4086 if ([source record] == nil)
4093 - (void) resetViewAnimated:(BOOL)animated {
4094 [list_ resetViewAnimated:animated];
4097 - (void) _leftButtonClicked {
4098 /*[book_ pushPage:[[[AddSourceView alloc]
4103 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4104 initWithTitle:@"Enter Cydia/APT URL"
4105 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4106 defaultButtonIndex:0
4111 [sheet addTextFieldWithValue:@"http://" label:@""];
4113 UITextTraits *traits = [[sheet textField] textTraits];
4114 [traits setAutoCapsType:0];
4115 [traits setPreferredKeyboardType:3];
4116 [traits setAutoCorrectionType:1];
4118 [sheet popupAlertAnimated:YES];
4121 - (void) _rightButtonClicked {
4122 UITable *table = [list_ table];
4123 BOOL editing = [table isRowDeletionEnabled];
4124 [table enableRowDeletion:!editing animated:YES];
4125 [book_ reloadButtonsForPage:self];
4128 - (NSString *) title {
4132 - (NSString *) backButtonTitle {
4136 - (NSString *) leftButtonTitle {
4137 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4140 - (NSString *) rightButtonTitle {
4141 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4144 - (RVUINavBarButtonStyle) rightButtonStyle {
4145 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4151 /* Installed View {{{ */
4152 @interface InstalledView : RVPage {
4153 _transient Database *database_;
4154 PackageTable *packages_;
4158 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4162 @implementation InstalledView
4165 [packages_ release];
4169 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4170 if ((self = [super initWithBook:book]) != nil) {
4171 database_ = database;
4173 packages_ = [[PackageTable alloc]
4177 filter:@selector(isInstalledAndVisible:)
4178 with:[NSNumber numberWithBool:YES]
4181 [self addSubview:packages_];
4185 - (void) resetViewAnimated:(BOOL)animated {
4186 [packages_ resetViewAnimated:animated];
4189 - (void) reloadData {
4190 [packages_ reloadData];
4193 - (void) _rightButtonClicked {
4194 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4195 [packages_ reloadData];
4197 [book_ reloadButtonsForPage:self];
4200 - (NSString *) title {
4201 return @"Installed";
4204 - (NSString *) backButtonTitle {
4208 - (NSString *) rightButtonTitle {
4209 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4212 - (RVUINavBarButtonStyle) rightButtonStyle {
4213 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4216 - (void) setDelegate:(id)delegate {
4217 [super setDelegate:delegate];
4218 [packages_ setDelegate:delegate];
4225 @interface HomeView : BrowserView {
4230 @implementation HomeView
4232 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4236 - (void) _leftButtonClicked {
4237 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4238 initWithTitle:@"About Cydia Installer"
4239 buttons:[NSArray arrayWithObjects:@"Close", nil]
4240 defaultButtonIndex:0
4246 @"Copyright (C) 2008\n"
4247 "Jay Freeman (saurik)\n"
4248 "saurik@saurik.com\n"
4249 "http://www.saurik.com/\n"
4252 "http://www.theokorigroup.com/\n"
4254 "College of Creative Studies,\n"
4255 "University of California,\n"
4257 "http://www.ccs.ucsb.edu/"
4260 [sheet popupAlertAnimated:YES];
4263 - (NSString *) leftButtonTitle {
4269 /* Manage View {{{ */
4270 @interface ManageView : BrowserView {
4275 @implementation ManageView
4277 - (NSString *) title {
4281 - (void) _leftButtonClicked {
4282 [delegate_ askForSettings];
4285 - (NSString *) leftButtonTitle {
4289 - (NSString *) rightButtonTitle {
4296 /* Browser Implementation {{{ */
4297 @implementation BrowserView
4300 WebView *webview = [webview_ webView];
4301 [webview setFrameLoadDelegate:nil];
4302 [webview setResourceLoadDelegate:nil];
4303 [webview setUIDelegate:nil];
4305 [scroller_ setDelegate:nil];
4306 [webview_ setDelegate:nil];
4308 [scroller_ release];
4311 [indicator_ release];
4317 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4318 [self loadRequest:[NSURLRequest
4321 timeoutInterval:30.0
4325 - (void) loadURL:(NSURL *)url {
4326 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4329 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4330 NSMutableURLRequest *copy = [request mutableCopy];
4332 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4333 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4334 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4337 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4342 - (void) loadRequest:(NSURLRequest *)request {
4344 [webview_ loadRequest:request];
4347 - (void) reloadURL {
4348 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4349 [urls_ removeLastObject];
4350 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4353 - (WebView *) webView {
4354 return [webview_ webView];
4357 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4358 [scroller_ setContentSize:frame.size];
4361 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4362 [self view:sender didSetFrame:frame];
4365 - (void) pushPage:(RVPage *)page {
4366 [self setBackButtonTitle:title_];
4367 [page setDelegate:delegate_];
4368 [book_ pushPage:page];
4371 - (void) getSpecial:(NSString *)href {
4374 if ([href hasPrefix:@"mailto:"])
4375 [delegate_ openURL:[NSURL URLWithString:href]];
4376 else if ([href isEqualToString:@"cydia://add-source"])
4377 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4378 else if ([href isEqualToString:@"cydia://sources"])
4379 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4380 else if ([href isEqualToString:@"cydia://packages"])
4381 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4382 else if ([href hasPrefix:@"apptapp://package/"]) {
4383 NSString *name = [href substringFromIndex:18];
4385 if (Package *package = [database_ packageWithName:name]) {
4386 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4387 [view setPackage:package];
4390 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4391 initWithTitle:@"Cannot Locate Package"
4392 buttons:[NSArray arrayWithObjects:@"Close", nil]
4393 defaultButtonIndex:0
4398 [sheet setBodyText:[NSString stringWithFormat:
4399 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4402 [sheet popupAlertAnimated:YES];
4407 [self pushPage:page];
4410 - (void) webView:(WebView *)sender willClickElement:(id)element {
4411 if ([[element localName] isEqualToString:@"img"])
4412 do if ((element = [element parentNode]) == nil)
4414 while (![[element localName] isEqualToString:@"a"]);
4415 if (![element respondsToSelector:@selector(href)])
4417 NSString *href = [element href];
4420 [self getSpecial:href];
4423 - (BOOL) isSpecialScheme:(NSString *)scheme {
4425 [scheme isEqualToString:@"apptapp"] ||
4426 [scheme isEqualToString:@"cydia"] ||
4427 [scheme isEqualToString:@"mailto"];
4430 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4431 NSURL *url = [request URL];
4432 if ([self isSpecialScheme:[url scheme]]) {
4433 [self getSpecial:[url absoluteString]];
4439 [book_ pushPage:self];
4442 return [self _addHeadersToRequest:request];
4445 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4446 if (request != nil) {
4447 NSString *scheme = [[request URL] scheme];
4448 if ([self isSpecialScheme:scheme])
4452 [self setBackButtonTitle:title_];
4454 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4455 [browser setDelegate:delegate_];
4457 if (request != nil) {
4458 [browser loadRequest:[self _addHeadersToRequest:request]];
4459 [book_ pushPage:browser];
4462 return [browser webView];
4465 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4466 title_ = [title retain];
4467 [self setTitle:title];
4470 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4471 if ([frame parentFrame] != nil)
4476 [indicator_ startAnimation];
4477 [self reloadButtons];
4479 if (title_ != nil) {
4484 [self setTitle:@"Loading..."];
4486 WebView *webview = [webview_ webView];
4487 NSString *href = [webview mainFrameURL];
4488 [urls_ addObject:[NSURL URLWithString:href]];
4490 CGRect webrect = [scroller_ frame];
4491 webrect.size.height = 0;
4492 [webview_ setFrame:webrect];
4495 - (void) _finishLoading {
4498 [indicator_ stopAnimation];
4499 [self reloadButtons];
4503 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4504 if ([frame parentFrame] != nil)
4506 [self _finishLoading];
4509 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4510 if ([frame parentFrame] != nil)
4512 [self _finishLoading];
4514 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4515 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4516 [[error localizedDescription] stringByAddingPercentEscapes]
4520 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4521 if ((self = [super initWithBook:book]) != nil) {
4522 database_ = database;
4525 struct CGRect bounds = [self bounds];
4527 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4528 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4529 [self addSubview:pinstripe];
4531 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4532 [self addSubview:scroller_];
4534 [scroller_ setScrollingEnabled:YES];
4535 [scroller_ setAdjustForContentSizeChange:YES];
4536 [scroller_ setClipsSubviews:YES];
4537 [scroller_ setAllowsRubberBanding:YES];
4538 [scroller_ setScrollDecelerationFactor:0.99];
4539 [scroller_ setDelegate:self];
4541 CGRect webrect = [scroller_ bounds];
4542 webrect.size.height = 0;
4544 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4545 [scroller_ addSubview:webview_];
4547 [webview_ setTilingEnabled:YES];
4548 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4549 [webview_ setAutoresizes:YES];
4550 [webview_ setDelegate:self];
4551 //[webview_ setEnabledGestures:2];
4553 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4554 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4555 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4557 Package *package([database_ packageWithName:@"cydia"]);
4558 NSString *application = package == nil ? @"Cydia" : [NSString
4559 stringWithFormat:@"Cydia/%@",
4563 WebView *webview = [webview_ webView];
4564 [webview setApplicationNameForUserAgent:application];
4565 [webview setFrameLoadDelegate:self];
4566 [webview setResourceLoadDelegate:self];
4567 [webview setUIDelegate:self];
4569 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4573 - (void) _rightButtonClicked {
4578 - (NSString *) rightButtonTitle {
4579 return loading_ ? @"" : @"Reload";
4582 - (NSString *) title {
4586 - (NSString *) backButtonTitle {
4590 - (void) setPageActive:(BOOL)active {
4592 [book_ addSubview:indicator_];
4594 [indicator_ removeFromSuperview];
4597 - (void) resetViewAnimated:(BOOL)animated {
4600 - (void) setPushed:(bool)pushed {
4607 @interface CYBook : RVBook <
4610 _transient Database *database_;
4612 UIProgressIndicator *indicator_;
4613 UITextLabel *prompt_;
4614 UIProgressBar *progress_;
4618 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4624 /* Install View {{{ */
4625 @interface InstallView : RVPage {
4626 _transient Database *database_;
4627 NSMutableArray *sections_;
4628 NSMutableArray *filtered_;
4629 UITransitionView *transition_;
4635 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4636 - (void) reloadData;
4641 @implementation InstallView
4644 [list_ setDataSource:nil];
4645 [list_ setDelegate:nil];
4647 [sections_ release];
4648 [filtered_ release];
4649 [transition_ release];
4651 [accessory_ release];
4655 - (int) numberOfRowsInTable:(UITable *)table {
4656 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4659 - (float) table:(UITable *)table heightForRow:(int)row {
4663 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4665 reusing = [[[SectionCell alloc] init] autorelease];
4666 [(SectionCell *)reusing setSection:(editing_ ?
4667 [sections_ objectAtIndex:row] :
4668 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4669 ) editing:editing_];
4673 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4677 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4681 - (void) tableRowSelected:(NSNotification *)notification {
4682 int row = [[notification object] selectedRow];
4693 title = @"All Packages";
4695 section = [filtered_ objectAtIndex:(row - 1)];
4696 name = [section name];
4702 title = @"(No Section)";
4706 PackageTable *table = [[[PackageTable alloc]
4710 filter:@selector(isVisiblyUninstalledInSection:)
4714 [table setDelegate:delegate_];
4716 [book_ pushPage:table];
4719 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4720 if ((self = [super initWithBook:book]) != nil) {
4721 database_ = database;
4723 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4724 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4726 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4727 [self addSubview:transition_];
4729 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4730 [transition_ transition:0 toView:list_];
4732 UITableColumn *column = [[[UITableColumn alloc]
4733 initWithTitle:@"Name"
4735 width:[self frame].size.width
4738 [list_ setDataSource:self];
4739 [list_ setSeparatorStyle:1];
4740 [list_ addTableColumn:column];
4741 [list_ setDelegate:self];
4742 [list_ setReusesTableCells:YES];
4748 - (void) reloadData {
4749 NSArray *packages = [database_ packages];
4751 [sections_ removeAllObjects];
4752 [filtered_ removeAllObjects];
4754 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4755 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4757 for (size_t i(0); i != [packages count]; ++i) {
4758 Package *package([packages objectAtIndex:i]);
4759 NSString *name([package section]);
4762 Section *section([sections objectForKey:name]);
4763 if (section == nil) {
4764 section = [[[Section alloc] initWithName:name] autorelease];
4765 [sections setObject:section forKey:name];
4769 if ([package valid] && [package installed] == nil && [package visible])
4770 [filtered addObject:package];
4773 [sections_ addObjectsFromArray:[sections allValues]];
4774 [sections_ sortUsingSelector:@selector(compareByName:)];
4776 [filtered sortUsingSelector:@selector(compareBySection:)];
4778 Section *section = nil;
4779 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4780 Package *package = [filtered objectAtIndex:offset];
4781 NSString *name = [package section];
4783 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4784 section = name == nil ?
4785 [[[Section alloc] initWithName:nil] autorelease] :
4786 [sections objectForKey:name];
4787 [filtered_ addObject:section];
4790 [section addToCount];
4796 - (void) resetView {
4798 [self _rightButtonClicked];
4801 - (void) resetViewAnimated:(BOOL)animated {
4802 [list_ resetViewAnimated:animated];
4805 - (void) _rightButtonClicked {
4806 if ((editing_ = !editing_))
4809 [delegate_ updateData];
4812 [book_ setTitle:[self title] forPage:self];
4813 [book_ reloadButtonsForPage:self];
4816 - (NSString *) title {
4817 return editing_ ? @"Section Visibility" : @"Install by Section";
4820 - (NSString *) backButtonTitle {
4824 - (NSString *) rightButtonTitle {
4825 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4828 - (UIView *) accessoryView {
4834 /* Changes View {{{ */
4835 @interface ChangesView : RVPage {
4836 _transient Database *database_;
4837 NSMutableArray *packages_;
4838 NSMutableArray *sections_;
4839 UISectionList *list_;
4843 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4844 - (void) reloadData;
4848 @implementation ChangesView
4851 [[list_ table] setDelegate:nil];
4852 [list_ setDataSource:nil];
4854 [packages_ release];
4855 [sections_ release];
4860 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4861 return [sections_ count];
4864 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4865 return [[sections_ objectAtIndex:section] name];
4868 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4869 return [[sections_ objectAtIndex:section] row];
4872 - (int) numberOfRowsInTable:(UITable *)table {
4873 return [packages_ count];
4876 - (float) table:(UITable *)table heightForRow:(int)row {
4877 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4880 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4882 reusing = [[[PackageCell alloc] init] autorelease];
4883 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4887 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4891 - (void) tableRowSelected:(NSNotification *)notification {
4892 int row = [[notification object] selectedRow];
4895 Package *package = [packages_ objectAtIndex:row];
4896 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4897 [view setDelegate:delegate_];
4898 [view setPackage:package];
4899 [book_ pushPage:view];
4902 - (void) _leftButtonClicked {
4903 [(CYBook *)book_ update];
4904 [self reloadButtons];
4907 - (void) _rightButtonClicked {
4908 [delegate_ distUpgrade];
4911 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4912 if ((self = [super initWithBook:book]) != nil) {
4913 database_ = database;
4915 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4916 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4918 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4919 [self addSubview:list_];
4921 [list_ setShouldHideHeaderInShortLists:NO];
4922 [list_ setDataSource:self];
4923 //[list_ setSectionListStyle:1];
4925 UITableColumn *column = [[[UITableColumn alloc]
4926 initWithTitle:@"Name"
4928 width:[self frame].size.width
4931 UITable *table = [list_ table];
4932 [table setSeparatorStyle:1];
4933 [table addTableColumn:column];
4934 [table setDelegate:self];
4935 [table setReusesTableCells:YES];
4941 - (void) reloadData {
4942 NSArray *packages = [database_ packages];
4944 [packages_ removeAllObjects];
4945 [sections_ removeAllObjects];
4947 for (size_t i(0); i != [packages count]; ++i) {
4948 Package *package([packages objectAtIndex:i]);
4951 [package installed] == nil && [package valid] && [package visible] ||
4952 [package upgradableAndEssential:NO]
4954 [packages_ addObject:package];
4957 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4959 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4960 Section *section = nil;
4963 bool unseens = false;
4965 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4967 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4968 Package *package = [packages_ objectAtIndex:offset];
4970 if ([package upgradableAndEssential:YES]) {
4972 [upgradable addToCount];
4975 NSDate *seen = [package seen];
4980 name = [@"n/a ?" retain];
4982 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4985 if (section == nil || ![[section name] isEqualToString:name]) {
4986 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4987 [sections_ addObject:section];
4991 [section addToCount];
4995 CFRelease(formatter);
4998 Section *last = [sections_ lastObject];
4999 size_t count = [last count];
5000 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5001 [sections_ removeLastObject];
5005 [sections_ insertObject:upgradable atIndex:0];
5008 [self reloadButtons];
5011 - (void) resetViewAnimated:(BOOL)animated {
5012 [list_ resetViewAnimated:animated];
5015 - (NSString *) leftButtonTitle {
5016 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5019 - (NSString *) rightButtonTitle {
5020 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5023 - (NSString *) title {
5029 /* Search View {{{ */
5030 @protocol SearchViewDelegate
5031 - (void) showKeyboard:(BOOL)show;
5034 @interface SearchView : RVPage {
5036 UISearchField *field_;
5037 UITransitionView *transition_;
5038 PackageTable *table_;
5039 UIPreferencesTable *advanced_;
5045 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5046 - (void) reloadData;
5050 @implementation SearchView
5054 [[field_ textTraits] setEditingDelegate:nil];
5056 [field_ setDelegate:nil];
5058 [accessory_ release];
5060 [transition_ release];
5062 [advanced_ release];
5067 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5071 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5073 case 0: return @"Advanced Search (Coming Soon!)";
5075 default: _assert(false);
5079 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5083 default: _assert(false);
5087 - (void) _showKeyboard:(BOOL)show {
5088 CGSize keysize = [UIKeyboard defaultSize];
5089 CGRect keydown = [book_ pageBounds];
5090 CGRect keyup = keydown;
5091 keyup.size.height -= keysize.height - ButtonBarHeight_;
5093 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5095 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5096 [animation setSignificantRectFields:8];
5099 [animation setStartFrame:keydown];
5100 [animation setEndFrame:keyup];
5102 [animation setStartFrame:keyup];
5103 [animation setEndFrame:keydown];
5106 UIAnimator *animator = [UIAnimator sharedAnimator];
5109 addAnimations:[NSArray arrayWithObjects:animation, nil]
5110 withDuration:(KeyboardTime_ - delay)
5115 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5118 [delegate_ showKeyboard:show];
5122 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5123 [self _showKeyboard:YES];
5126 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5127 [self _showKeyboard:NO];
5130 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5132 NSString *text([field_ text]);
5133 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5139 - (void) textFieldClearButtonPressed:(UITextField *)field {
5143 - (void) keyboardInputShouldDelete:(id)input {
5147 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5148 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5152 [field_ resignFirstResponder];
5157 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5158 if ((self = [super initWithBook:book]) != nil) {
5159 CGRect pageBounds = [book_ pageBounds];
5161 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5162 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5163 [self addSubview:pinstripe];*/
5165 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5166 [self addSubview:transition_];
5168 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5170 [advanced_ setReusesTableCells:YES];
5171 [advanced_ setDataSource:self];
5172 [advanced_ reloadData];
5174 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5175 CGColor dimmed(space_, 0, 0, 0, 0.5);
5176 [dimmed_ setBackgroundColor:dimmed];
5178 table_ = [[PackageTable alloc]
5182 filter:@selector(isVisiblySearchedForBy:)
5186 [table_ setShouldHideHeaderInShortLists:NO];
5187 [transition_ transition:0 toView:table_];
5196 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5203 [self bounds].size.width - area.origin.x - 18;
5205 area.size.height = [UISearchField defaultHeight];
5207 field_ = [[UISearchField alloc] initWithFrame:area];
5209 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
5210 [field_ setFont:font];
5213 [field_ setPlaceholder:@"Package Names & Descriptions"];
5214 [field_ setDelegate:self];
5217 [field_ setPaddingTop:3];
5219 [field_ setPaddingTop:5];
5222 UITextTraits *traits = [field_ textTraits];
5223 [traits setAutoCapsType:0];
5224 [traits setAutoCorrectionType:1];
5225 [traits setReturnKeyType:6];
5228 [traits setEditingDelegate:self];
5231 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5233 accessory_ = [[UIView alloc] initWithFrame:accrect];
5234 [accessory_ addSubview:field_];
5236 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5237 [configure setShowPressFeedback:YES];
5238 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5239 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5240 [accessory_ addSubview:configure];*/
5246 LKAnimation *animation = [LKTransition animation];
5247 [animation setType:@"oglFlip"];
5248 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5249 [animation setFillMode:@"extended"];
5250 [animation setTransitionFlags:3];
5251 [animation setDuration:10];
5252 [animation setSpeed:0.35];
5253 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5254 [[transition_ _layer] addAnimation:animation forKey:0];
5255 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5256 flipped_ = !flipped_;
5260 - (void) configurePushed {
5261 [field_ resignFirstResponder];
5265 - (void) resetViewAnimated:(BOOL)animated {
5268 [table_ resetViewAnimated:animated];
5271 - (void) reloadData {
5274 [table_ setObject:[field_ text]];
5275 [table_ reloadData];
5276 [table_ resetCursor];
5279 - (UIView *) accessoryView {
5283 - (NSString *) title {
5287 - (NSString *) backButtonTitle {
5291 - (void) setDelegate:(id)delegate {
5292 [table_ setDelegate:delegate];
5293 [super setDelegate:delegate];
5299 @implementation CYBook
5303 [indicator_ release];
5305 [progress_ release];
5309 - (NSString *) getTitleForPage:(RVPage *)page {
5310 return Simplify([super getTitleForPage:page]);
5318 [navbar_ setPrompt:@""];
5319 [navbar_ addSubview:overlay_];
5320 [indicator_ startAnimation];
5321 [prompt_ setText:@"Updating Database..."];
5322 [progress_ setProgress:0];
5327 detachNewThreadSelector:@selector(_update)
5336 [overlay_ removeFromSuperview];
5337 [indicator_ stopAnimation];
5338 [delegate_ reloadData];
5340 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5343 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5344 if ((self = [super initWithFrame:frame]) != nil) {
5345 database_ = database;
5348 [navbar_ setBarStyle:1];
5350 CGRect ovrrect = [navbar_ bounds];
5351 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5353 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5355 UIProgressIndicatorStyle style = Advanced_ ?
5356 kUIProgressIndicatorStyleSmallWhite :
5357 kUIProgressIndicatorStyleSmallBlack;
5359 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5360 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5361 CGRect indrect = {{indoffset, indoffset}, indsize};
5363 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5364 [indicator_ setStyle:style];
5365 [overlay_ addSubview:indicator_];
5367 CGSize prmsize = {200, indsize.width + 4};
5370 indoffset * 2 + indsize.width,
5374 (ovrrect.size.height - prmsize.height) / 2
5377 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
5379 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5381 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
5382 [prompt_ setBackgroundColor:Clear_];
5383 [prompt_ setFont:font];
5387 [overlay_ addSubview:prompt_];
5389 CGSize prgsize = {75, 100};
5392 ovrrect.size.width - prgsize.width - 10,
5393 (ovrrect.size.height - prgsize.height) / 2
5396 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5397 [progress_ setStyle:0];
5398 [overlay_ addSubview:progress_];
5403 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5406 status.setDelegate(self);
5408 [database_ updateWithStatus:status];
5411 performSelectorOnMainThread:@selector(_update_)
5419 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5420 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5423 - (void) setProgressTitle:(NSString *)title {
5425 performSelectorOnMainThread:@selector(_setProgressTitle:)
5431 - (void) setProgressPercent:(float)percent {
5434 - (void) addProgressOutput:(NSString *)output {
5436 performSelectorOnMainThread:@selector(_addProgressOutput:)
5442 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5446 - (void) _setProgressTitle:(NSString *)title {
5447 [prompt_ setText:[title stringByAppendingString:@"..."]];
5450 - (void) _addProgressOutput:(NSString *)output {
5455 @interface Cydia : UIApplication <
5456 ConfirmationViewDelegate,
5457 ProgressViewDelegate,
5466 UIButtonBar *buttonbar_;
5468 ConfirmationView *confirm_;
5470 NSMutableArray *essential_;
5471 NSMutableArray *broken_;
5473 Database *database_;
5474 ProgressView *progress_;
5478 UIKeyboard *keyboard_;
5479 UIProgressHUD *hud_;
5481 InstallView *install_;
5482 ChangesView *changes_;
5483 ManageView *manage_;
5484 SearchView *search_;
5489 @implementation Cydia
5492 if ([broken_ count] != 0) {
5493 int count = [broken_ count];
5495 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5496 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5497 buttons:[NSArray arrayWithObjects:
5499 @"Ignore (Temporary)",
5501 defaultButtonIndex:0
5506 [sheet setBodyText:@"When the shell scripts associated with packages fail, they are left in a bad state known as either half-configured or half-installed. These errors don't go away and instead continue to cause issues. These scripts can be deleted and the packages forcibly removed."];
5507 [sheet popupAlertAnimated:YES];
5508 } else if (!Ignored_ && [essential_ count] != 0) {
5509 int count = [essential_ count];
5511 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5512 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5513 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5514 defaultButtonIndex:0
5519 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5520 [sheet popupAlertAnimated:YES];
5524 - (void) _reloadData {
5525 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5526 [hud setText:@"Reloading Data"];
5527 [overlay_ addSubview:hud];
5530 [database_ reloadData];
5534 [essential_ removeAllObjects];
5535 [broken_ removeAllObjects];
5537 NSArray *packages = [database_ packages];
5538 for (int i(0), e([packages count]); i != e; ++i) {
5539 Package *package = [packages objectAtIndex:i];
5541 [broken_ addObject:package];
5542 if ([package upgradableAndEssential:NO]) {
5543 if ([package essential])
5544 [essential_ addObject:package];
5550 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5551 [buttonbar_ setBadgeValue:badge forButton:3];
5552 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5553 [buttonbar_ setBadgeAnimated:YES forButton:3];
5554 [self setApplicationBadge:badge];
5556 [buttonbar_ setBadgeValue:nil forButton:3];
5557 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5558 [buttonbar_ setBadgeAnimated:NO forButton:3];
5559 [self removeApplicationBadge];
5564 /*if ([packages count] == 0);
5573 [hud removeFromSuperview];*/
5576 - (void) _saveConfig {
5578 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5583 - (void) updateData {
5586 /* XXX: this is just stupid */
5588 [install_ reloadData];
5590 [changes_ reloadData];
5592 [search_ reloadData];
5602 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5603 _assert(file != NULL);
5605 NSArray *keys = [Sources_ allKeys];
5607 for (int i(0), e([keys count]); i != e; ++i) {
5608 NSString *key = [keys objectAtIndex:i];
5609 NSDictionary *source = [Sources_ objectForKey:key];
5611 fprintf(file, "%s %s %s\n",
5612 [[source objectForKey:@"Type"] UTF8String],
5613 [[source objectForKey:@"URI"] UTF8String],
5614 [[source objectForKey:@"Distribution"] UTF8String]
5623 detachNewThreadSelector:@selector(update_)
5626 title:@"Updating Sources..."
5630 - (void) reloadData {
5631 @synchronized (self) {
5632 if (confirm_ == nil)
5638 pkgProblemResolver *resolver = [database_ resolver];
5640 resolver->InstallProtect();
5641 if (!resolver->Resolve(true))
5646 [database_ prepare];
5648 if ([database_ cache]->BrokenCount() == 0)
5649 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5651 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5652 NSArray *packages = [database_ packages];
5654 for (size_t i(0); i != [packages count]; ++i) {
5655 Package *package = [packages objectAtIndex:i];
5656 if ([package broken])
5657 [broken addObject:[package name]];
5660 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5661 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5662 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5663 defaultButtonIndex:0
5668 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5669 [sheet popupAlertAnimated:YES];
5675 - (void) installPackage:(Package *)package {
5676 @synchronized (self) {
5683 - (void) removePackage:(Package *)package {
5684 @synchronized (self) {
5691 - (void) distUpgrade {
5692 @synchronized (self) {
5693 [database_ upgrade];
5699 @synchronized (self) {
5707 [overlay_ removeFromSuperview];
5711 detachNewThreadSelector:@selector(perform)
5718 - (void) bootstrap_ {
5720 [database_ upgrade];
5721 [database_ prepare];
5722 [database_ perform];
5725 - (void) bootstrap {
5727 detachNewThreadSelector:@selector(bootstrap_)
5730 title:@"Bootstrap Install..."
5734 - (void) progressViewIsComplete:(ProgressView *)progress {
5735 @synchronized (self) {
5738 if (confirm_ != nil) {
5739 [underlay_ addSubview:overlay_];
5740 [confirm_ removeFromSuperview];
5747 - (void) setPage:(RVPage *)page {
5748 [page resetViewAnimated:NO];
5749 [page setDelegate:self];
5750 [book_ setPage:page];
5753 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5754 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5755 [browser loadURL:url];
5759 - (void) _setHomePage {
5760 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5763 - (void) buttonBarItemTapped:(id)sender {
5764 unsigned tag = [sender tag];
5766 [book_ resetViewAnimated:YES];
5768 } else if (tag_ == 2 && tag != 2)
5769 [install_ resetView];
5772 case 1: [self _setHomePage]; break;
5774 case 2: [self setPage:install_]; break;
5775 case 3: [self setPage:changes_]; break;
5776 case 4: [self setPage:manage_]; break;
5777 case 5: [self setPage:search_]; break;
5779 default: _assert(false);
5785 - (void) fixSpringBoard {
5786 pid_t pid = ExecFork();
5790 if (pid_t child = fork()) {
5791 waitpid(child, NULL, 0);
5793 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5794 perror("launchctl unload");
5798 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5799 perror("launchctl load");
5804 - (void) applicationWillSuspend {
5809 [self fixSpringBoard];
5813 [super applicationWillSuspend];
5816 - (void) askForSettings {
5817 UIAlertSheet *role = [[[UIAlertSheet alloc]
5818 initWithTitle:@"Who Are You?"
5819 buttons:[NSArray arrayWithObjects:
5820 @"User (Graphical Only)",
5821 @"Hacker (+ Command Line)",
5822 @"Developer (No Filters)",
5824 defaultButtonIndex:-1
5829 [role setBodyText:@"Not all of the packages available via Cydia are designed to be used by all users. Please categorize yourself so that Cydia can apply helpful filters.\n\nThis choice can be changed from \"Settings\" under the \"Manage\" tab."];
5830 [role popupAlertAnimated:YES];
5835 [self setStatusBarShowsProgress:NO];
5838 [hud_ removeFromSuperview];
5842 pid_t pid = ExecFork();
5844 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5845 perror("launchctl stop");
5852 [self askForSettings];
5856 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5858 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5859 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5860 0, 0, screenrect.size.width, screenrect.size.height - 48
5861 ) database:database_];
5863 [book_ setDelegate:self];
5865 [overlay_ addSubview:book_];
5867 NSArray *buttonitems = [NSArray arrayWithObjects:
5868 [NSDictionary dictionaryWithObjectsAndKeys:
5869 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5870 @"home-up.png", kUIButtonBarButtonInfo,
5871 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5872 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5873 self, kUIButtonBarButtonTarget,
5874 @"Home", kUIButtonBarButtonTitle,
5875 @"0", kUIButtonBarButtonType,
5878 [NSDictionary dictionaryWithObjectsAndKeys:
5879 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5880 @"install-up.png", kUIButtonBarButtonInfo,
5881 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5882 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5883 self, kUIButtonBarButtonTarget,
5884 @"Sections", kUIButtonBarButtonTitle,
5885 @"0", kUIButtonBarButtonType,
5888 [NSDictionary dictionaryWithObjectsAndKeys:
5889 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5890 @"changes-up.png", kUIButtonBarButtonInfo,
5891 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5892 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5893 self, kUIButtonBarButtonTarget,
5894 @"Changes", kUIButtonBarButtonTitle,
5895 @"0", kUIButtonBarButtonType,
5898 [NSDictionary dictionaryWithObjectsAndKeys:
5899 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5900 @"manage-up.png", kUIButtonBarButtonInfo,
5901 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5902 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5903 self, kUIButtonBarButtonTarget,
5904 @"Manage", kUIButtonBarButtonTitle,
5905 @"0", kUIButtonBarButtonType,
5908 [NSDictionary dictionaryWithObjectsAndKeys:
5909 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5910 @"search-up.png", kUIButtonBarButtonInfo,
5911 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5912 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5913 self, kUIButtonBarButtonTarget,
5914 @"Search", kUIButtonBarButtonTitle,
5915 @"0", kUIButtonBarButtonType,
5919 buttonbar_ = [[UIButtonBar alloc]
5921 withFrame:CGRectMake(
5922 0, screenrect.size.height - ButtonBarHeight_,
5923 screenrect.size.width, ButtonBarHeight_
5925 withItemList:buttonitems
5928 [buttonbar_ setDelegate:self];
5929 [buttonbar_ setBarStyle:1];
5930 [buttonbar_ setButtonBarTrackingMode:2];
5932 int buttons[5] = {1, 2, 3, 4, 5};
5933 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5934 [buttonbar_ showButtonGroup:0 withDuration:0];
5936 for (int i = 0; i != 5; ++i)
5937 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5938 i * 64 + 2, 1, 60, ButtonBarHeight_
5941 [buttonbar_ showSelectionForButton:1];
5942 [overlay_ addSubview:buttonbar_];
5944 [UIKeyboard initImplementationNow];
5945 CGSize keysize = [UIKeyboard defaultSize];
5946 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5947 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5948 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5949 [overlay_ addSubview:keyboard_];
5951 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5952 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5953 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5955 manage_ = (ManageView *) [[self
5956 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5957 withClass:[ManageView class]
5961 [underlay_ addSubview:overlay_];
5968 [self _setHomePage];
5971 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5972 NSString *context = [sheet context];
5973 if ([context isEqualToString:@"fixhalf"])
5976 @synchronized (self) {
5977 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5978 Package *broken = [broken_ objectAtIndex:i];
5981 NSString *id = [broken id];
5982 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5983 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5984 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5985 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5994 [broken_ removeAllObjects];
6001 else if ([context isEqualToString:@"role"]) {
6003 case 1: Role_ = @"User"; break;
6004 case 2: Role_ = @"Hacker"; break;
6005 case 3: Role_ = @"Developer"; break;
6012 bool reset = Settings_ != nil;
6014 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6018 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6026 } else if ([context isEqualToString:@"upgrade"])
6029 @synchronized (self) {
6030 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6031 Package *essential = [essential_ objectAtIndex:i];
6032 [essential install];
6051 - (void) reorganize {
6052 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6053 system("/usr/libexec/cydia/free.sh");
6054 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6058 - (void) applicationSuspend:(__GSEvent *)event {
6059 if (hud_ == nil && ![progress_ isRunning])
6060 [super applicationSuspend:event];
6063 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6065 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6068 - (void) _setSuspended:(BOOL)value {
6070 [super _setSuspended:value];
6073 - (UIProgressHUD *) addProgressHUD {
6074 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6076 [underlay_ addSubview:hud];
6080 - (void) applicationDidFinishLaunching:(id)unused {
6081 Font12_ = [[UIFont systemFontOfSize:12] retain];
6082 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6083 Font14_ = [[UIFont systemFontOfSize:14] retain];
6084 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6085 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6087 _assert(pkgInitConfig(*_config));
6088 _assert(pkgInitSystem(*_config, _system));
6093 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6094 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6096 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6097 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6099 [window_ orderFront:self];
6100 [window_ makeKey:self];
6101 [window_ _setHidden:NO];
6103 database_ = [[Database alloc] init];
6104 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6105 [database_ setDelegate:progress_];
6106 [window_ setContentView:progress_];
6108 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6109 [progress_ setContentView:underlay_];
6111 [progress_ resetView];
6114 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6115 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6116 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6117 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6118 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6119 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
6121 [self setIdleTimerDisabled:YES];
6123 hud_ = [self addProgressHUD];
6124 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6126 [self setStatusBarShowsProgress:YES];
6129 detachNewThreadSelector:@selector(reorganize)
6137 - (void) showKeyboard:(BOOL)show {
6138 CGSize keysize = [UIKeyboard defaultSize];
6139 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6140 CGRect keyup = keydown;
6141 keyup.origin.y -= keysize.height;
6143 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6144 [animation setSignificantRectFields:2];
6147 [animation setStartFrame:keydown];
6148 [animation setEndFrame:keyup];
6149 [keyboard_ activate];
6151 [animation setStartFrame:keyup];
6152 [animation setEndFrame:keydown];
6153 [keyboard_ deactivate];
6156 [[UIAnimator sharedAnimator]
6157 addAnimations:[NSArray arrayWithObjects:animation, nil]
6158 withDuration:KeyboardTime_
6163 - (void) slideUp:(UIAlertSheet *)alert {
6165 [alert presentSheetFromButtonBar:buttonbar_];
6167 [alert presentSheetInView:overlay_];
6172 void AddPreferences(NSString *plist) {
6173 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6175 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6176 _assert(settings != NULL);
6177 NSMutableArray *items = [settings objectForKey:@"items"];
6181 for (size_t i(0); i != [items count]; ++i) {
6182 NSMutableDictionary *item([items objectAtIndex:i]);
6183 NSString *label = [item objectForKey:@"label"];
6184 if (label != nil && [label isEqualToString:@"Cydia"]) {
6191 for (size_t i(0); i != [items count]; ++i) {
6192 NSDictionary *item([items objectAtIndex:i]);
6193 NSString *label = [item objectForKey:@"label"];
6194 if (label != nil && [label isEqualToString:@"General"]) {
6195 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6196 @"CydiaSettings", @"bundle",
6197 @"PSLinkCell", @"cell",
6198 [NSNumber numberWithBool:YES], @"hasIcon",
6199 [NSNumber numberWithBool:YES], @"isController",
6201 nil] atIndex:(i + 1)];
6207 _assert([settings writeToFile:plist atomically:YES] == YES);
6214 id Alloc_(id self, SEL selector) {
6215 id object = alloc_(self, selector);
6216 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6221 id Dealloc_(id self, SEL selector) {
6222 id object = dealloc_(self, selector);
6223 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6227 int main(int argc, char *argv[]) {
6228 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6230 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6232 Home_ = NSHomeDirectory();
6235 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6236 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6237 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6238 Sounds_Keyboard_ = [keyboard boolValue];
6244 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6245 _assert(errno == ENOENT);
6246 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6247 _assert(errno == ENOENT);
6249 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6250 alloc_ = alloc->method_imp;
6251 alloc->method_imp = (IMP) &Alloc_;*/
6253 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6254 dealloc_ = dealloc->method_imp;
6255 dealloc->method_imp = (IMP) &Dealloc_;*/
6257 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6258 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6259 Firmware_ = strdup([prover UTF8String]);
6260 NSArray *versions = [prover componentsSeparatedByString:@"."];
6261 int count = [versions count];
6262 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6263 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6264 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6269 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6270 char *machine = new char[size];
6271 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6274 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6275 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6276 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6277 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6281 IOObjectRelease(service);
6284 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6285 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6287 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6288 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6290 Settings_ = [Metadata_ objectForKey:@"Settings"];
6292 Packages_ = [Metadata_ objectForKey:@"Packages"];
6293 Sections_ = [Metadata_ objectForKey:@"Sections"];
6294 Sources_ = [Metadata_ objectForKey:@"Sources"];
6297 if (Settings_ != nil)
6298 Role_ = [Settings_ objectForKey:@"Role"];
6300 if (Packages_ == nil) {
6301 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6302 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6305 if (Sections_ == nil) {
6306 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6307 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6310 if (Sources_ == nil) {
6311 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6312 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6315 if (access("/User", F_OK) != 0)
6316 system("/usr/libexec/cydia/firmware.sh");
6318 Locale_ = CFLocaleCopyCurrent();
6319 space_ = CGColorSpaceCreateDeviceRGB();
6321 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6322 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6323 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6324 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6325 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6326 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6327 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6329 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6331 int value = UIApplicationMain(argc, argv, [Cydia class]);
6333 CGColorSpaceRelease(space_);