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 @protocol PackageViewDelegate
3152 - (void) performPackage:(Package *)package;
3155 @interface PackageView : RVPage {
3156 _transient Database *database_;
3157 UIPreferencesTable *table_;
3160 UITextView *description_;
3161 NSMutableArray *buttons_;
3164 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3165 - (void) setPackage:(Package *)package;
3169 @implementation PackageView
3172 [table_ setDataSource:nil];
3173 [table_ setDelegate:nil];
3175 if (package_ != nil)
3179 if (description_ != nil)
3180 [description_ release];
3186 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
3188 if ([package_ installed] != nil)
3190 if ([package_ source] != nil)
3195 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
3198 else if ([package_ installed] != nil && group-- == 0)
3199 return @"Installed Package";
3200 else if (group-- == 0)
3201 return @"Package Details";
3202 else if ([package_ source] != nil && group-- == 0)
3203 return @"Source Information";
3204 else _assert(false);
3207 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
3208 if (description_ == nil || group != 0 || row != ([package_ author] == nil ? 1 : 2))
3211 return [description_ visibleTextRect].size.height + TextViewOffset_;
3214 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
3217 if ([package_ author] != nil)
3219 if (description_ != nil)
3221 if ([package_ website] != nil)
3224 } else if ([package_ installed] != nil && group-- == 0)
3226 else if (group-- == 0) {
3228 if ([package_ size] != 0)
3230 if ([package_ maintainer] != nil)
3232 if ([package_ sponsor] != nil)
3234 if ([package_ relationships] != nil)
3236 if ([[package_ source] trusted])
3239 } else if ([package_ source] != nil && group-- == 0) {
3240 Source *source = [package_ source];
3241 NSString *description = [source description];
3243 if (description != nil && ![description isEqualToString:[source label]])
3245 if ([source origin] != nil)
3248 } else _assert(false);
3251 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3252 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3253 [cell setShowSelection:NO];
3257 } else if (row-- == 0) {
3258 [cell setTitle:[package_ name]];
3259 [cell setValue:[package_ latest]];
3260 } else if ([package_ author] != nil && row-- == 0) {
3261 [cell setTitle:@"Author"];
3262 [cell setValue:[[package_ author] name]];
3263 [cell setShowDisclosure:YES];
3264 [cell setShowSelection:YES];
3265 } else if (description_ != nil && row-- == 0) {
3266 [cell addSubview:description_];
3267 } else if ([package_ website] != nil && row-- == 0) {
3268 [cell setTitle:@"More Information"];
3269 [cell setShowDisclosure:YES];
3270 [cell setShowSelection:YES];
3271 } else _assert(false);
3272 } else if ([package_ installed] != nil && group-- == 0) {
3274 } else if (row-- == 0) {
3275 [cell setTitle:@"Version"];
3276 NSString *installed([package_ installed]);
3277 [cell setValue:(installed == nil ? @"n/a" : installed)];
3278 } else if (row-- == 0) {
3279 [cell setTitle:@"Filesystem Content"];
3280 [cell setShowDisclosure:YES];
3281 [cell setShowSelection:YES];
3282 } else _assert(false);
3283 } else if (group-- == 0) {
3285 } else if (row-- == 0) {
3286 [cell setTitle:@"Identifier"];
3287 [cell setValue:[package_ id]];
3288 } else if (row-- == 0) {
3289 [cell setTitle:@"Section"];
3290 NSString *section([package_ section]);
3291 [cell setValue:(section == nil ? @"n/a" : section)];
3292 } else if ([package_ size] != 0 && row-- == 0) {
3293 [cell setTitle:@"Expanded Size"];
3294 [cell setValue:SizeString([package_ size])];
3295 } else if ([package_ maintainer] != nil && row-- == 0) {
3296 [cell setTitle:@"Maintainer"];
3297 [cell setValue:[[package_ maintainer] name]];
3298 [cell setShowDisclosure:YES];
3299 [cell setShowSelection:YES];
3300 } else if ([package_ sponsor] != nil && row-- == 0) {
3301 [cell setTitle:@"Sponsor"];
3302 [cell setValue:[[package_ sponsor] name]];
3303 [cell setShowDisclosure:YES];
3304 [cell setShowSelection:YES];
3305 } else if ([package_ relationships] != nil && row-- == 0) {
3306 [cell setTitle:@"Package Relationships"];
3307 [cell setShowDisclosure:YES];
3308 [cell setShowSelection:YES];
3309 } else if ([[package_ source] trusted] && row-- == 0) {
3310 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3311 [cell setValue:@"This package has been signed."];
3312 } else _assert(false);
3313 } else if ([package_ source] != nil && group-- == 0) {
3314 Source *source = [package_ source];
3315 NSString *description = [source description];
3318 } else if (row-- == 0) {
3319 NSString *label = [source label];
3321 label = [source uri];
3322 [cell setTitle:label];
3323 [cell setValue:[source version]];
3324 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3325 [cell setValue:description];
3326 } else if ([source origin] != nil && row-- == 0) {
3327 [cell setTitle:@"Origin"];
3328 [cell setValue:[source origin]];
3329 } else _assert(false);
3330 } else _assert(false);
3335 - (BOOL) canSelectRow:(int)row {
3339 - (void) tableRowSelected:(NSNotification *)notification {
3340 int row = [table_ selectedRow];
3344 #define _else else goto _label; return; } _label:
3348 } else if (row-- == 0) {
3349 } else if ([package_ author] != nil && row-- == 0) {
3350 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3351 [[package_ author] email],
3352 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3354 ] stringByAddingPercentEscapes]
3356 } else if (description_ != nil && row-- == 0) {
3357 } else if ([package_ website] != nil && row-- == 0) {
3358 NSURL *url = [NSURL URLWithString:[package_ website]];
3359 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3360 [browser setDelegate:delegate_];
3361 [book_ pushPage:browser];
3362 [browser loadURL:url];
3363 } _else if ([package_ installed] != nil) {
3365 } else if (row-- == 0) {
3366 } else if (row-- == 0) {
3367 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3368 [files setDelegate:delegate_];
3369 [files setPackage:package_];
3370 [book_ pushPage:files];
3373 } else if (row-- == 0) {
3374 } else if (row-- == 0) {
3375 } else if ([package_ size] != 0 && row-- == 0) {
3376 } else if ([package_ maintainer] != nil && row-- == 0) {
3377 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3378 [[package_ maintainer] email],
3379 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3381 ] stringByAddingPercentEscapes]
3383 } else if ([package_ sponsor] != nil && row-- == 0) {
3384 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3385 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3386 [browser setDelegate:delegate_];
3387 [book_ pushPage:browser];
3388 [browser loadURL:url];
3389 } else if ([package_ relationships] != nil && row-- == 0) {
3390 } else if ([[package_ source] trusted] && row-- == 0) {
3391 } _else if ([package_ source] != nil) {
3392 Source *source = [package_ source];
3393 NSString *description = [source description];
3396 } else if (row-- == 0) {
3397 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3398 } else if ([source origin] != nil && row-- == 0) {
3399 } _else _assert(false);
3404 - (void) _clickButtonWithName:(NSString *)name {
3405 if ([name isEqualToString:@"Install"])
3406 [delegate_ installPackage:package_];
3407 else if ([name isEqualToString:@"Reinstall"])
3408 [delegate_ installPackage:package_];
3409 else if ([name isEqualToString:@"Remove"])
3410 [delegate_ removePackage:package_];
3411 else if ([name isEqualToString:@"Upgrade"])
3412 [delegate_ installPackage:package_];
3413 else _assert(false);
3416 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3417 int count = [buttons_ count];
3418 _assert(count != 0);
3419 _assert(button <= count + 1);
3421 if (count != button - 1)
3422 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3427 - (void) _rightButtonClicked {
3428 int count = [buttons_ count];
3429 _assert(count != 0);
3432 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3434 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3435 [buttons addObjectsFromArray:buttons_];
3436 [buttons addObject:@"Cancel"];
3438 [delegate_ slideUp:[[[UIAlertSheet alloc]
3441 defaultButtonIndex:2
3448 - (NSString *) rightButtonTitle {
3449 int count = [buttons_ count];
3450 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3453 - (NSString *) title {
3457 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3458 if ((self = [super initWithBook:book]) != nil) {
3459 database_ = database;
3461 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3462 [self addSubview:table_];
3464 [table_ setDataSource:self];
3465 [table_ setDelegate:self];
3467 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3471 - (void) setPackage:(Package *)package {
3472 if (package_ != nil) {
3473 [package_ autorelease];
3482 if (description_ != nil) {
3483 [description_ release];
3487 [buttons_ removeAllObjects];
3489 if (package != nil) {
3490 package_ = [package retain];
3491 name_ = [[package id] retain];
3493 NSString *description([package description]);
3494 if (description == nil)
3495 description = [package tagline];
3496 if (description != nil) {
3497 description_ = [GetTextView(description, 12, true) retain];
3498 [description_ setTextColor:Black_];
3501 [table_ reloadData];
3503 if ([package_ source] == nil);
3504 else if ([package_ upgradableAndEssential:NO])
3505 [buttons_ addObject:@"Upgrade"];
3506 else if ([package_ installed] == nil)
3507 [buttons_ addObject:@"Install"];
3509 [buttons_ addObject:@"Reinstall"];
3510 if ([package_ installed] != nil)
3511 [buttons_ addObject:@"Remove"];
3515 - (void) resetViewAnimated:(BOOL)animated {
3516 [table_ resetViewAnimated:animated];
3519 - (void) reloadData {
3520 [self setPackage:[database_ packageWithName:name_]];
3521 [self reloadButtons];
3526 /* Package Table {{{ */
3527 @interface PackageTable : RVPage {
3528 _transient Database *database_;
3532 NSMutableArray *packages_;
3533 NSMutableArray *sections_;
3534 UISectionList *list_;
3537 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3539 - (void) setDelegate:(id)delegate;
3540 - (void) setObject:(id)object;
3542 - (void) reloadData;
3543 - (void) resetCursor;
3545 - (UISectionList *) list;
3547 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3551 @implementation PackageTable
3554 [list_ setDataSource:nil];
3559 [packages_ release];
3560 [sections_ release];
3565 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3566 return [sections_ count];
3569 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3570 return [[sections_ objectAtIndex:section] name];
3573 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3574 return [[sections_ objectAtIndex:section] row];
3577 - (int) numberOfRowsInTable:(UITable *)table {
3578 return [packages_ count];
3581 - (float) table:(UITable *)table heightForRow:(int)row {
3582 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3585 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3587 reusing = [[[PackageCell alloc] init] autorelease];
3588 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3592 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3596 - (void) tableRowSelected:(NSNotification *)notification {
3597 int row = [[notification object] selectedRow];
3601 Package *package = [packages_ objectAtIndex:row];
3602 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3603 [view setDelegate:delegate_];
3604 [view setPackage:package];
3605 [book_ pushPage:view];
3608 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3609 if ((self = [super initWithBook:book]) != nil) {
3610 database_ = database;
3611 title_ = [title retain];
3613 object_ = object == nil ? nil : [object retain];
3615 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3616 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3618 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3619 [list_ setDataSource:self];
3621 UITableColumn *column = [[[UITableColumn alloc]
3622 initWithTitle:@"Name"
3624 width:[self frame].size.width
3627 UITable *table = [list_ table];
3628 [table setSeparatorStyle:1];
3629 [table addTableColumn:column];
3630 [table setDelegate:self];
3631 [table setReusesTableCells:YES];
3633 [self addSubview:list_];
3638 - (void) setDelegate:(id)delegate {
3639 delegate_ = delegate;
3642 - (void) setObject:(id)object {
3648 object_ = [object retain];
3651 - (void) reloadData {
3652 NSArray *packages = [database_ packages];
3654 [packages_ removeAllObjects];
3655 [sections_ removeAllObjects];
3657 for (size_t i(0); i != [packages count]; ++i) {
3658 Package *package([packages objectAtIndex:i]);
3659 if ([[package performSelector:filter_ withObject:object_] boolValue])
3660 [packages_ addObject:package];
3663 Section *section = nil;
3665 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3666 Package *package = [packages_ objectAtIndex:offset];
3667 NSString *name = [package index];
3669 if (section == nil || ![[section name] isEqualToString:name]) {
3670 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3671 [sections_ addObject:section];
3674 [section addToCount];
3680 - (NSString *) title {
3684 - (void) resetViewAnimated:(BOOL)animated {
3685 [list_ resetViewAnimated:animated];
3688 - (void) resetCursor {
3689 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3692 - (UISectionList *) list {
3696 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3697 [list_ setShouldHideHeaderInShortLists:hide];
3703 /* Add Source View {{{ */
3704 @interface AddSourceView : RVPage {
3705 _transient Database *database_;
3708 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3712 @implementation AddSourceView
3714 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3715 if ((self = [super initWithBook:book]) != nil) {
3716 database_ = database;
3722 /* Source Cell {{{ */
3723 @interface SourceCell : UITableCell {
3726 NSString *description_;
3732 - (SourceCell *) initWithSource:(Source *)source;
3736 @implementation SourceCell
3741 [description_ release];
3746 - (SourceCell *) initWithSource:(Source *)source {
3747 if ((self = [super init]) != nil) {
3749 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
3751 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
3752 icon_ = [icon_ retain];
3754 origin_ = [[source name] retain];
3755 label_ = [[source uri] retain];
3756 description_ = [[source description] retain];
3760 - (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3762 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
3769 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3773 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
3777 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
3779 [super drawContentInRect:rect selected:selected];
3784 /* Source Table {{{ */
3785 @interface SourceTable : RVPage {
3786 _transient Database *database_;
3787 UISectionList *list_;
3788 NSMutableArray *sources_;
3789 UIAlertSheet *alert_;
3793 UIProgressHUD *hud_;
3796 //NSURLConnection *installer_;
3797 NSURLConnection *trivial_bz2_;
3798 NSURLConnection *trivial_gz_;
3799 //NSURLConnection *automatic_;
3804 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3808 @implementation SourceTable
3810 - (void) _deallocConnection:(NSURLConnection *)connection {
3811 if (connection != nil) {
3812 [connection cancel];
3813 //[connection setDelegate:nil];
3814 [connection release];
3819 [[list_ table] setDelegate:nil];
3820 [list_ setDataSource:nil];
3829 //[self _deallocConnection:installer_];
3830 [self _deallocConnection:trivial_gz_];
3831 [self _deallocConnection:trivial_bz2_];
3832 //[self _deallocConnection:automatic_];
3839 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3840 return offset_ == 0 ? 1 : 2;
3843 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3844 switch (section + (offset_ == 0 ? 1 : 0)) {
3845 case 0: return @"Entered by User";
3846 case 1: return @"Installed by Packages";
3854 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3855 switch (section + (offset_ == 0 ? 1 : 0)) {
3857 case 1: return offset_;
3865 - (int) numberOfRowsInTable:(UITable *)table {
3866 return [sources_ count];
3869 - (float) table:(UITable *)table heightForRow:(int)row {
3870 Source *source = [sources_ objectAtIndex:row];
3871 return [source description] == nil ? 56 : 73;
3874 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3875 Source *source = [sources_ objectAtIndex:row];
3876 // XXX: weird warning, stupid selectors ;P
3877 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
3880 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3884 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3888 - (void) tableRowSelected:(NSNotification*)notification {
3889 UITable *table([list_ table]);
3890 int row([table selectedRow]);
3894 Source *source = [sources_ objectAtIndex:row];
3896 PackageTable *packages = [[[PackageTable alloc]
3899 title:[source label]
3900 filter:@selector(isVisibleInSource:)
3904 [packages setDelegate:delegate_];
3906 [book_ pushPage:packages];
3909 - (BOOL) table:(UITable *)table canDeleteRow:(int)row {
3910 Source *source = [sources_ objectAtIndex:row];
3911 return [source record] != nil;
3914 - (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
3915 [[list_ table] setDeleteConfirmationRow:row];
3918 - (void) table:(UITable *)table deleteRow:(int)row {
3919 Source *source = [sources_ objectAtIndex:row];
3920 [Sources_ removeObjectForKey:[source key]];
3921 [delegate_ syncData];
3924 - (void) _endConnection:(NSURLConnection *)connection {
3925 NSURLConnection **field = NULL;
3926 if (connection == trivial_bz2_)
3927 field = &trivial_bz2_;
3928 else if (connection == trivial_gz_)
3929 field = &trivial_gz_;
3930 _assert(field != NULL);
3931 [connection release];
3935 trivial_bz2_ == nil &&
3938 [delegate_ setStatusBarShowsProgress:NO];
3941 [hud_ removeFromSuperview];
3946 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
3949 @"./", @"Distribution",
3950 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
3952 [delegate_ syncData];
3953 } else if (error_ != nil) {
3954 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3955 initWithTitle:@"Verification Error"
3956 buttons:[NSArray arrayWithObjects:@"OK", nil]
3957 defaultButtonIndex:0
3962 [sheet setBodyText:[error_ localizedDescription]];
3963 [sheet popupAlertAnimated:YES];
3965 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3966 initWithTitle:@"Did not Find Repository"
3967 buttons:[NSArray arrayWithObjects:@"OK", nil]
3968 defaultButtonIndex:0
3973 [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."];
3974 [sheet popupAlertAnimated:YES];
3980 if (error_ != nil) {
3987 - (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
3988 switch ([response statusCode]) {
3994 - (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
3995 fprintf(stderr, "connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
3997 error_ = [error retain];
3998 [self _endConnection:connection];
4001 - (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4002 [self _endConnection:connection];
4005 - (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4006 NSMutableURLRequest *request = [NSMutableURLRequest
4007 requestWithURL:[NSURL URLWithString:href]
4008 cachePolicy:NSURLRequestUseProtocolCachePolicy
4009 timeoutInterval:20.0
4012 [request setHTTPMethod:method];
4014 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4017 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4018 NSString *context = [sheet context];
4019 if ([context isEqualToString:@"source"])
4022 NSString *href = [[sheet textField] text];
4024 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4026 if (![href hasSuffix:@"/"])
4027 href_ = [href stringByAppendingString:@"/"];
4030 href_ = [href_ retain];
4032 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4033 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4034 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4038 hud_ = [delegate_ addProgressHUD];
4039 [hud_ setText:@"Verifying URL"];
4052 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4053 if ((self = [super initWithBook:book]) != nil) {
4054 database_ = database;
4055 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4057 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4058 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4059 [list_ setShouldHideHeaderInShortLists:NO];
4061 [self addSubview:list_];
4062 [list_ setDataSource:self];
4064 UITableColumn *column = [[UITableColumn alloc]
4065 initWithTitle:@"Name"
4067 width:[self frame].size.width
4070 UITable *table = [list_ table];
4071 [table setSeparatorStyle:1];
4072 [table addTableColumn:column];
4073 [table setDelegate:self];
4079 - (void) reloadData {
4081 _assert(list.ReadMainList());
4083 [sources_ removeAllObjects];
4084 [sources_ addObjectsFromArray:[database_ sources]];
4085 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
4087 int count = [sources_ count];
4088 for (offset_ = 0; offset_ != count; ++offset_) {
4089 Source *source = [sources_ objectAtIndex:offset_];
4090 if ([source record] == nil)
4097 - (void) resetViewAnimated:(BOOL)animated {
4098 [list_ resetViewAnimated:animated];
4101 - (void) _leftButtonClicked {
4102 /*[book_ pushPage:[[[AddSourceView alloc]
4107 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4108 initWithTitle:@"Enter Cydia/APT URL"
4109 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4110 defaultButtonIndex:0
4115 [sheet addTextFieldWithValue:@"http://" label:@""];
4117 UITextTraits *traits = [[sheet textField] textTraits];
4118 [traits setAutoCapsType:0];
4119 [traits setPreferredKeyboardType:3];
4120 [traits setAutoCorrectionType:1];
4122 [sheet popupAlertAnimated:YES];
4125 - (void) _rightButtonClicked {
4126 UITable *table = [list_ table];
4127 BOOL editing = [table isRowDeletionEnabled];
4128 [table enableRowDeletion:!editing animated:YES];
4129 [book_ reloadButtonsForPage:self];
4132 - (NSString *) title {
4136 - (NSString *) backButtonTitle {
4140 - (NSString *) leftButtonTitle {
4141 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4144 - (NSString *) rightButtonTitle {
4145 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4148 - (RVUINavBarButtonStyle) rightButtonStyle {
4149 return [[list_ table] isRowDeletionEnabled] ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4155 /* Installed View {{{ */
4156 @interface InstalledView : RVPage {
4157 _transient Database *database_;
4158 PackageTable *packages_;
4162 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4166 @implementation InstalledView
4169 [packages_ release];
4173 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4174 if ((self = [super initWithBook:book]) != nil) {
4175 database_ = database;
4177 packages_ = [[PackageTable alloc]
4181 filter:@selector(isInstalledAndVisible:)
4182 with:[NSNumber numberWithBool:YES]
4185 [self addSubview:packages_];
4189 - (void) resetViewAnimated:(BOOL)animated {
4190 [packages_ resetViewAnimated:animated];
4193 - (void) reloadData {
4194 [packages_ reloadData];
4197 - (void) _rightButtonClicked {
4198 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4199 [packages_ reloadData];
4201 [book_ reloadButtonsForPage:self];
4204 - (NSString *) title {
4205 return @"Installed";
4208 - (NSString *) backButtonTitle {
4212 - (NSString *) rightButtonTitle {
4213 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4216 - (RVUINavBarButtonStyle) rightButtonStyle {
4217 return expert_ ? RVUINavBarButtonStyleHighlighted : RVUINavBarButtonStyleNormal;
4220 - (void) setDelegate:(id)delegate {
4221 [super setDelegate:delegate];
4222 [packages_ setDelegate:delegate];
4228 @interface HomeView : BrowserView {
4233 @implementation HomeView
4235 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4239 - (void) _leftButtonClicked {
4240 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4241 initWithTitle:@"About Cydia Installer"
4242 buttons:[NSArray arrayWithObjects:@"Close", nil]
4243 defaultButtonIndex:0
4249 @"Copyright (C) 2008\n"
4250 "Jay Freeman (saurik)\n"
4251 "saurik@saurik.com\n"
4252 "http://www.saurik.com/\n"
4255 "http://www.theokorigroup.com/\n"
4257 "College of Creative Studies,\n"
4258 "University of California,\n"
4260 "http://www.ccs.ucsb.edu/"
4263 [sheet popupAlertAnimated:YES];
4266 - (NSString *) leftButtonTitle {
4272 @interface ManageView : BrowserView {
4277 @implementation ManageView
4279 - (NSString *) title {
4283 - (void) _leftButtonClicked {
4284 [delegate_ askForSettings];
4287 - (NSString *) leftButtonTitle {
4291 - (NSString *) rightButtonTitle {
4297 /* Browser Implementation {{{ */
4298 @implementation BrowserView
4301 WebView *webview = [webview_ webView];
4302 [webview setFrameLoadDelegate:nil];
4303 [webview setResourceLoadDelegate:nil];
4304 [webview setUIDelegate:nil];
4306 [scroller_ setDelegate:nil];
4307 [webview_ setDelegate:nil];
4309 [scroller_ release];
4312 [indicator_ release];
4318 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4319 [self loadRequest:[NSURLRequest
4322 timeoutInterval:30.0
4326 - (void) loadURL:(NSURL *)url {
4327 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4330 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
4331 NSMutableURLRequest *copy = [request mutableCopy];
4333 [copy addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
4334 [copy addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
4335 [copy addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
4338 [copy addValue:Role_ forHTTPHeaderField:@"X-Role"];
4343 - (void) loadRequest:(NSURLRequest *)request {
4345 [webview_ loadRequest:request];
4348 - (void) reloadURL {
4349 NSURL *url = [[[urls_ lastObject] retain] autorelease];
4350 [urls_ removeLastObject];
4351 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
4354 - (WebView *) webView {
4355 return [webview_ webView];
4358 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4359 [scroller_ setContentSize:frame.size];
4362 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4363 [self view:sender didSetFrame:frame];
4366 - (void) pushPage:(RVPage *)page {
4367 [self setBackButtonTitle:title_];
4368 [page setDelegate:delegate_];
4369 [book_ pushPage:page];
4372 - (void) getSpecial:(NSString *)href {
4375 if ([href hasPrefix:@"mailto:"])
4376 [delegate_ openURL:[NSURL URLWithString:href]];
4377 else if ([href isEqualToString:@"cydia://add-source"])
4378 page = [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
4379 else if ([href isEqualToString:@"cydia://sources"])
4380 page = [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
4381 else if ([href isEqualToString:@"cydia://packages"])
4382 page = [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
4383 else if ([href hasPrefix:@"apptapp://package/"]) {
4384 NSString *name = [href substringFromIndex:18];
4386 if (Package *package = [database_ packageWithName:name]) {
4387 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4388 [view setPackage:package];
4391 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4392 initWithTitle:@"Cannot Locate Package"
4393 buttons:[NSArray arrayWithObjects:@"Close", nil]
4394 defaultButtonIndex:0
4399 [sheet setBodyText:[NSString stringWithFormat:
4400 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
4403 [sheet popupAlertAnimated:YES];
4408 [self pushPage:page];
4411 - (void) webView:(WebView *)sender willClickElement:(id)element {
4412 if ([[element localName] isEqualToString:@"img"])
4413 do if ((element = [element parentNode]) == nil)
4415 while (![[element localName] isEqualToString:@"a"]);
4416 if (![element respondsToSelector:@selector(href)])
4418 NSString *href = [element href];
4421 [self getSpecial:href];
4424 - (BOOL) isSpecialScheme:(NSString *)scheme {
4426 [scheme isEqualToString:@"apptapp"] ||
4427 [scheme isEqualToString:@"cydia"] ||
4428 [scheme isEqualToString:@"mailto"];
4431 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
4432 NSURL *url = [request URL];
4433 if ([self isSpecialScheme:[url scheme]]) {
4434 [self getSpecial:[url absoluteString]];
4440 [book_ pushPage:self];
4443 return [self _addHeadersToRequest:request];
4446 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
4447 if (request != nil) {
4448 NSString *scheme = [[request URL] scheme];
4449 if ([self isSpecialScheme:scheme])
4453 [self setBackButtonTitle:title_];
4455 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4456 [browser setDelegate:delegate_];
4458 if (request != nil) {
4459 [browser loadRequest:[self _addHeadersToRequest:request]];
4460 [book_ pushPage:browser];
4463 return [browser webView];
4466 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
4467 title_ = [title retain];
4468 [self setTitle:title];
4471 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
4472 if ([frame parentFrame] != nil)
4477 [indicator_ startAnimation];
4478 [self reloadButtons];
4480 if (title_ != nil) {
4485 [self setTitle:@"Loading..."];
4487 WebView *webview = [webview_ webView];
4488 NSString *href = [webview mainFrameURL];
4489 [urls_ addObject:[NSURL URLWithString:href]];
4491 CGRect webrect = [scroller_ frame];
4492 webrect.size.height = 0;
4493 [webview_ setFrame:webrect];
4496 - (void) _finishLoading {
4499 [indicator_ stopAnimation];
4500 [self reloadButtons];
4504 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
4505 if ([frame parentFrame] != nil)
4507 [self _finishLoading];
4510 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
4511 if ([frame parentFrame] != nil)
4513 [self _finishLoading];
4515 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
4516 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
4517 [[error localizedDescription] stringByAddingPercentEscapes]
4521 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4522 if ((self = [super initWithBook:book]) != nil) {
4523 database_ = database;
4526 struct CGRect bounds = [self bounds];
4528 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
4529 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4530 [self addSubview:pinstripe];
4532 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
4533 [self addSubview:scroller_];
4535 [scroller_ setScrollingEnabled:YES];
4536 [scroller_ setAdjustForContentSizeChange:YES];
4537 [scroller_ setClipsSubviews:YES];
4538 [scroller_ setAllowsRubberBanding:YES];
4539 [scroller_ setScrollDecelerationFactor:0.99];
4540 [scroller_ setDelegate:self];
4542 CGRect webrect = [scroller_ bounds];
4543 webrect.size.height = 0;
4545 webview_ = [[UIWebView alloc] initWithFrame:webrect];
4546 [scroller_ addSubview:webview_];
4548 [webview_ setTilingEnabled:YES];
4549 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
4550 [webview_ setAutoresizes:YES];
4551 [webview_ setDelegate:self];
4552 //[webview_ setEnabledGestures:2];
4554 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
4555 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
4556 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
4558 Package *package([database_ packageWithName:@"cydia"]);
4559 NSString *application = package == nil ? @"Cydia" : [NSString
4560 stringWithFormat:@"Cydia/%@",
4564 WebView *webview = [webview_ webView];
4565 [webview setApplicationNameForUserAgent:application];
4566 [webview setFrameLoadDelegate:self];
4567 [webview setResourceLoadDelegate:self];
4568 [webview setUIDelegate:self];
4570 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
4574 - (void) _rightButtonClicked {
4579 - (NSString *) rightButtonTitle {
4580 return loading_ ? @"" : @"Reload";
4583 - (NSString *) title {
4587 - (NSString *) backButtonTitle {
4591 - (void) setPageActive:(BOOL)active {
4593 [book_ addSubview:indicator_];
4595 [indicator_ removeFromSuperview];
4598 - (void) resetViewAnimated:(BOOL)animated {
4601 - (void) setPushed:(bool)pushed {
4608 @interface CYBook : RVBook <
4611 _transient Database *database_;
4613 UIProgressIndicator *indicator_;
4614 UITextLabel *prompt_;
4615 UIProgressBar *progress_;
4619 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
4625 /* Install View {{{ */
4626 @interface InstallView : RVPage {
4627 _transient Database *database_;
4628 NSMutableArray *sections_;
4629 NSMutableArray *filtered_;
4630 UITransitionView *transition_;
4636 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4637 - (void) reloadData;
4642 @implementation InstallView
4645 [list_ setDataSource:nil];
4646 [list_ setDelegate:nil];
4648 [sections_ release];
4649 [filtered_ release];
4650 [transition_ release];
4652 [accessory_ release];
4656 - (int) numberOfRowsInTable:(UITable *)table {
4657 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4660 - (float) table:(UITable *)table heightForRow:(int)row {
4664 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4666 reusing = [[[SectionCell alloc] init] autorelease];
4667 [(SectionCell *)reusing setSection:(editing_ ?
4668 [sections_ objectAtIndex:row] :
4669 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4670 ) editing:editing_];
4674 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4678 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4682 - (void) tableRowSelected:(NSNotification *)notification {
4683 int row = [[notification object] selectedRow];
4694 title = @"All Packages";
4696 section = [filtered_ objectAtIndex:(row - 1)];
4697 name = [section name];
4703 title = @"(No Section)";
4707 PackageTable *table = [[[PackageTable alloc]
4711 filter:@selector(isVisiblyUninstalledInSection:)
4715 [table setDelegate:delegate_];
4717 [book_ pushPage:table];
4720 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4721 if ((self = [super initWithBook:book]) != nil) {
4722 database_ = database;
4724 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4725 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4727 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4728 [self addSubview:transition_];
4730 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4731 [transition_ transition:0 toView:list_];
4733 UITableColumn *column = [[[UITableColumn alloc]
4734 initWithTitle:@"Name"
4736 width:[self frame].size.width
4739 [list_ setDataSource:self];
4740 [list_ setSeparatorStyle:1];
4741 [list_ addTableColumn:column];
4742 [list_ setDelegate:self];
4743 [list_ setReusesTableCells:YES];
4749 - (void) reloadData {
4750 NSArray *packages = [database_ packages];
4752 [sections_ removeAllObjects];
4753 [filtered_ removeAllObjects];
4755 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4756 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4758 for (size_t i(0); i != [packages count]; ++i) {
4759 Package *package([packages objectAtIndex:i]);
4760 NSString *name([package section]);
4763 Section *section([sections objectForKey:name]);
4764 if (section == nil) {
4765 section = [[[Section alloc] initWithName:name] autorelease];
4766 [sections setObject:section forKey:name];
4770 if ([package valid] && [package installed] == nil && [package visible])
4771 [filtered addObject:package];
4774 [sections_ addObjectsFromArray:[sections allValues]];
4775 [sections_ sortUsingSelector:@selector(compareByName:)];
4777 [filtered sortUsingSelector:@selector(compareBySection:)];
4779 Section *section = nil;
4780 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4781 Package *package = [filtered objectAtIndex:offset];
4782 NSString *name = [package section];
4784 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4785 section = name == nil ?
4786 [[[Section alloc] initWithName:nil] autorelease] :
4787 [sections objectForKey:name];
4788 [filtered_ addObject:section];
4791 [section addToCount];
4797 - (void) resetView {
4799 [self _rightButtonClicked];
4802 - (void) resetViewAnimated:(BOOL)animated {
4803 [list_ resetViewAnimated:animated];
4806 - (void) _rightButtonClicked {
4807 if ((editing_ = !editing_))
4810 [delegate_ updateData];
4813 [book_ setTitle:[self title] forPage:self];
4814 [book_ reloadButtonsForPage:self];
4817 - (NSString *) title {
4818 return editing_ ? @"Section Visibility" : @"Install by Section";
4821 - (NSString *) backButtonTitle {
4825 - (NSString *) rightButtonTitle {
4826 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4829 - (UIView *) accessoryView {
4835 /* Changes View {{{ */
4836 @interface ChangesView : RVPage {
4837 _transient Database *database_;
4838 NSMutableArray *packages_;
4839 NSMutableArray *sections_;
4840 UISectionList *list_;
4844 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4845 - (void) reloadData;
4849 @implementation ChangesView
4852 [[list_ table] setDelegate:nil];
4853 [list_ setDataSource:nil];
4855 [packages_ release];
4856 [sections_ release];
4861 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4862 return [sections_ count];
4865 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4866 return [[sections_ objectAtIndex:section] name];
4869 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4870 return [[sections_ objectAtIndex:section] row];
4873 - (int) numberOfRowsInTable:(UITable *)table {
4874 return [packages_ count];
4877 - (float) table:(UITable *)table heightForRow:(int)row {
4878 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4881 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4883 reusing = [[[PackageCell alloc] init] autorelease];
4884 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4888 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4892 - (void) tableRowSelected:(NSNotification *)notification {
4893 int row = [[notification object] selectedRow];
4896 Package *package = [packages_ objectAtIndex:row];
4897 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4898 [view setDelegate:delegate_];
4899 [view setPackage:package];
4900 [book_ pushPage:view];
4903 - (void) _leftButtonClicked {
4904 [(CYBook *)book_ update];
4905 [self reloadButtons];
4908 - (void) _rightButtonClicked {
4909 [delegate_ distUpgrade];
4912 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4913 if ((self = [super initWithBook:book]) != nil) {
4914 database_ = database;
4916 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4917 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4919 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4920 [self addSubview:list_];
4922 [list_ setShouldHideHeaderInShortLists:NO];
4923 [list_ setDataSource:self];
4924 //[list_ setSectionListStyle:1];
4926 UITableColumn *column = [[[UITableColumn alloc]
4927 initWithTitle:@"Name"
4929 width:[self frame].size.width
4932 UITable *table = [list_ table];
4933 [table setSeparatorStyle:1];
4934 [table addTableColumn:column];
4935 [table setDelegate:self];
4936 [table setReusesTableCells:YES];
4942 - (void) reloadData {
4943 NSArray *packages = [database_ packages];
4945 [packages_ removeAllObjects];
4946 [sections_ removeAllObjects];
4948 for (size_t i(0); i != [packages count]; ++i) {
4949 Package *package([packages objectAtIndex:i]);
4952 [package installed] == nil && [package valid] && [package visible] ||
4953 [package upgradableAndEssential:NO]
4955 [packages_ addObject:package];
4958 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4960 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4961 Section *section = nil;
4964 bool unseens = false;
4966 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4968 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4969 Package *package = [packages_ objectAtIndex:offset];
4971 if ([package upgradableAndEssential:YES]) {
4973 [upgradable addToCount];
4976 NSDate *seen = [package seen];
4981 name = [@"n/a ?" retain];
4983 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4986 if (section == nil || ![[section name] isEqualToString:name]) {
4987 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4988 [sections_ addObject:section];
4992 [section addToCount];
4996 CFRelease(formatter);
4999 Section *last = [sections_ lastObject];
5000 size_t count = [last count];
5001 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
5002 [sections_ removeLastObject];
5006 [sections_ insertObject:upgradable atIndex:0];
5009 [self reloadButtons];
5012 - (void) resetViewAnimated:(BOOL)animated {
5013 [list_ resetViewAnimated:animated];
5016 - (NSString *) leftButtonTitle {
5017 return [(CYBook *)book_ updating] ? nil : @"Refresh";
5020 - (NSString *) rightButtonTitle {
5021 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
5024 - (NSString *) title {
5030 /* Search View {{{ */
5031 @protocol SearchViewDelegate
5032 - (void) showKeyboard:(BOOL)show;
5035 @interface SearchView : RVPage {
5037 UISearchField *field_;
5038 UITransitionView *transition_;
5039 PackageTable *table_;
5040 UIPreferencesTable *advanced_;
5046 - (id) initWithBook:(RVBook *)book database:(Database *)database;
5047 - (void) reloadData;
5051 @implementation SearchView
5055 [[field_ textTraits] setEditingDelegate:nil];
5057 [field_ setDelegate:nil];
5059 [accessory_ release];
5061 [transition_ release];
5063 [advanced_ release];
5068 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
5072 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
5074 case 0: return @"Advanced Search (Coming Soon!)";
5076 default: _assert(false);
5080 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
5084 default: _assert(false);
5088 - (void) _showKeyboard:(BOOL)show {
5089 CGSize keysize = [UIKeyboard defaultSize];
5090 CGRect keydown = [book_ pageBounds];
5091 CGRect keyup = keydown;
5092 keyup.size.height -= keysize.height - ButtonBarHeight_;
5094 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
5096 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
5097 [animation setSignificantRectFields:8];
5100 [animation setStartFrame:keydown];
5101 [animation setEndFrame:keyup];
5103 [animation setStartFrame:keyup];
5104 [animation setEndFrame:keydown];
5107 UIAnimator *animator = [UIAnimator sharedAnimator];
5110 addAnimations:[NSArray arrayWithObjects:animation, nil]
5111 withDuration:(KeyboardTime_ - delay)
5116 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
5119 [delegate_ showKeyboard:show];
5123 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
5124 [self _showKeyboard:YES];
5127 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
5128 [self _showKeyboard:NO];
5131 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
5133 NSString *text([field_ text]);
5134 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
5140 - (void) textFieldClearButtonPressed:(UITextField *)field {
5144 - (void) keyboardInputShouldDelete:(id)input {
5148 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
5149 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
5153 [field_ resignFirstResponder];
5158 - (id) initWithBook:(RVBook *)book database:(Database *)database {
5159 if ((self = [super initWithBook:book]) != nil) {
5160 CGRect pageBounds = [book_ pageBounds];
5162 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
5163 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
5164 [self addSubview:pinstripe];*/
5166 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
5167 [self addSubview:transition_];
5169 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
5171 [advanced_ setReusesTableCells:YES];
5172 [advanced_ setDataSource:self];
5173 [advanced_ reloadData];
5175 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
5176 CGColor dimmed(space_, 0, 0, 0, 0.5);
5177 [dimmed_ setBackgroundColor:dimmed];
5179 table_ = [[PackageTable alloc]
5183 filter:@selector(isVisiblySearchedForBy:)
5187 [table_ setShouldHideHeaderInShortLists:NO];
5188 [transition_ transition:0 toView:table_];
5197 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
5204 [self bounds].size.width - area.origin.x - 18;
5206 area.size.height = [UISearchField defaultHeight];
5208 field_ = [[UISearchField alloc] initWithFrame:area];
5210 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
5211 [field_ setFont:font];
5214 [field_ setPlaceholder:@"Package Names & Descriptions"];
5215 [field_ setDelegate:self];
5218 [field_ setPaddingTop:3];
5220 [field_ setPaddingTop:5];
5223 UITextTraits *traits = [field_ textTraits];
5224 [traits setAutoCapsType:0];
5225 [traits setAutoCorrectionType:1];
5226 [traits setReturnKeyType:6];
5229 [traits setEditingDelegate:self];
5232 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
5234 accessory_ = [[UIView alloc] initWithFrame:accrect];
5235 [accessory_ addSubview:field_];
5237 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
5238 [configure setShowPressFeedback:YES];
5239 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
5240 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
5241 [accessory_ addSubview:configure];*/
5247 LKAnimation *animation = [LKTransition animation];
5248 [animation setType:@"oglFlip"];
5249 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
5250 [animation setFillMode:@"extended"];
5251 [animation setTransitionFlags:3];
5252 [animation setDuration:10];
5253 [animation setSpeed:0.35];
5254 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
5255 [[transition_ _layer] addAnimation:animation forKey:0];
5256 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
5257 flipped_ = !flipped_;
5261 - (void) configurePushed {
5262 [field_ resignFirstResponder];
5266 - (void) resetViewAnimated:(BOOL)animated {
5269 [table_ resetViewAnimated:animated];
5272 - (void) reloadData {
5275 [table_ setObject:[field_ text]];
5276 [table_ reloadData];
5277 [table_ resetCursor];
5280 - (UIView *) accessoryView {
5284 - (NSString *) title {
5288 - (NSString *) backButtonTitle {
5292 - (void) setDelegate:(id)delegate {
5293 [table_ setDelegate:delegate];
5294 [super setDelegate:delegate];
5300 @implementation CYBook
5304 [indicator_ release];
5306 [progress_ release];
5310 - (NSString *) getTitleForPage:(RVPage *)page {
5311 return Simplify([super getTitleForPage:page]);
5319 [navbar_ setPrompt:@""];
5320 [navbar_ addSubview:overlay_];
5321 [indicator_ startAnimation];
5322 [prompt_ setText:@"Updating Database..."];
5323 [progress_ setProgress:0];
5328 detachNewThreadSelector:@selector(_update)
5337 [overlay_ removeFromSuperview];
5338 [indicator_ stopAnimation];
5339 [delegate_ reloadData];
5341 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
5344 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
5345 if ((self = [super initWithFrame:frame]) != nil) {
5346 database_ = database;
5349 [navbar_ setBarStyle:1];
5351 CGRect ovrrect = [navbar_ bounds];
5352 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5354 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5356 UIProgressIndicatorStyle style = Advanced_ ?
5357 kUIProgressIndicatorStyleSmallWhite :
5358 kUIProgressIndicatorStyleSmallBlack;
5360 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5361 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5362 CGRect indrect = {{indoffset, indoffset}, indsize};
5364 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5365 [indicator_ setStyle:style];
5366 [overlay_ addSubview:indicator_];
5368 CGSize prmsize = {200, indsize.width + 4};
5371 indoffset * 2 + indsize.width,
5375 (ovrrect.size.height - prmsize.height) / 2
5378 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
5380 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5382 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
5383 [prompt_ setBackgroundColor:Clear_];
5384 [prompt_ setFont:font];
5388 [overlay_ addSubview:prompt_];
5390 CGSize prgsize = {75, 100};
5393 ovrrect.size.width - prgsize.width - 10,
5394 (ovrrect.size.height - prgsize.height) / 2
5397 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5398 [progress_ setStyle:0];
5399 [overlay_ addSubview:progress_];
5404 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5407 status.setDelegate(self);
5409 [database_ updateWithStatus:status];
5412 performSelectorOnMainThread:@selector(_update_)
5420 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5421 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5424 - (void) setProgressTitle:(NSString *)title {
5426 performSelectorOnMainThread:@selector(_setProgressTitle:)
5432 - (void) setProgressPercent:(float)percent {
5435 - (void) addProgressOutput:(NSString *)output {
5437 performSelectorOnMainThread:@selector(_addProgressOutput:)
5443 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5447 - (void) _setProgressTitle:(NSString *)title {
5448 [prompt_ setText:[title stringByAppendingString:@"..."]];
5451 - (void) _addProgressOutput:(NSString *)output {
5456 @interface Cydia : UIApplication <
5457 ConfirmationViewDelegate,
5458 ProgressViewDelegate,
5467 UIButtonBar *buttonbar_;
5469 ConfirmationView *confirm_;
5471 NSMutableArray *essential_;
5472 NSMutableArray *broken_;
5474 Database *database_;
5475 ProgressView *progress_;
5479 UIKeyboard *keyboard_;
5480 UIProgressHUD *hud_;
5482 InstallView *install_;
5483 ChangesView *changes_;
5484 ManageView *manage_;
5485 SearchView *search_;
5490 @implementation Cydia
5493 if ([broken_ count] != 0) {
5494 int count = [broken_ count];
5496 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5497 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5498 buttons:[NSArray arrayWithObjects:
5500 @"Ignore (Temporary)",
5502 defaultButtonIndex:0
5507 [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."];
5508 [sheet popupAlertAnimated:YES];
5509 } else if (!Ignored_ && [essential_ count] != 0) {
5510 int count = [essential_ count];
5512 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5513 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5514 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5515 defaultButtonIndex:0
5520 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5521 [sheet popupAlertAnimated:YES];
5525 - (void) _reloadData {
5526 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5527 [hud setText:@"Reloading Data"];
5528 [overlay_ addSubview:hud];
5531 [database_ reloadData];
5535 [essential_ removeAllObjects];
5536 [broken_ removeAllObjects];
5538 NSArray *packages = [database_ packages];
5539 for (int i(0), e([packages count]); i != e; ++i) {
5540 Package *package = [packages objectAtIndex:i];
5542 [broken_ addObject:package];
5543 if ([package upgradableAndEssential:NO]) {
5544 if ([package essential])
5545 [essential_ addObject:package];
5551 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5552 [buttonbar_ setBadgeValue:badge forButton:3];
5553 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5554 [buttonbar_ setBadgeAnimated:YES forButton:3];
5555 [self setApplicationBadge:badge];
5557 [buttonbar_ setBadgeValue:nil forButton:3];
5558 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5559 [buttonbar_ setBadgeAnimated:NO forButton:3];
5560 [self removeApplicationBadge];
5565 /*if ([packages count] == 0);
5574 [hud removeFromSuperview];*/
5577 - (void) _saveConfig {
5579 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5584 - (void) updateData {
5587 /* XXX: this is just stupid */
5589 [install_ reloadData];
5591 [changes_ reloadData];
5593 [search_ reloadData];
5603 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
5604 _assert(file != NULL);
5606 NSArray *keys = [Sources_ allKeys];
5608 for (int i(0), e([keys count]); i != e; ++i) {
5609 NSString *key = [keys objectAtIndex:i];
5610 NSDictionary *source = [Sources_ objectForKey:key];
5612 fprintf(file, "%s %s %s\n",
5613 [[source objectForKey:@"Type"] UTF8String],
5614 [[source objectForKey:@"URI"] UTF8String],
5615 [[source objectForKey:@"Distribution"] UTF8String]
5624 detachNewThreadSelector:@selector(update_)
5627 title:@"Updating Sources..."
5631 - (void) reloadData {
5632 @synchronized (self) {
5633 if (confirm_ == nil)
5639 pkgProblemResolver *resolver = [database_ resolver];
5641 resolver->InstallProtect();
5642 if (!resolver->Resolve(true))
5647 [database_ prepare];
5649 if ([database_ cache]->BrokenCount() == 0)
5650 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5652 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5653 NSArray *packages = [database_ packages];
5655 for (size_t i(0); i != [packages count]; ++i) {
5656 Package *package = [packages objectAtIndex:i];
5657 if ([package broken])
5658 [broken addObject:[package name]];
5661 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5662 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5663 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5664 defaultButtonIndex:0
5669 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5670 [sheet popupAlertAnimated:YES];
5676 - (void) installPackage:(Package *)package {
5677 @synchronized (self) {
5684 - (void) removePackage:(Package *)package {
5685 @synchronized (self) {
5692 - (void) distUpgrade {
5693 @synchronized (self) {
5694 [database_ upgrade];
5700 @synchronized (self) {
5708 [overlay_ removeFromSuperview];
5712 detachNewThreadSelector:@selector(perform)
5719 - (void) bootstrap_ {
5721 [database_ upgrade];
5722 [database_ prepare];
5723 [database_ perform];
5726 - (void) bootstrap {
5728 detachNewThreadSelector:@selector(bootstrap_)
5731 title:@"Bootstrap Install..."
5735 - (void) progressViewIsComplete:(ProgressView *)progress {
5736 @synchronized (self) {
5739 if (confirm_ != nil) {
5740 [underlay_ addSubview:overlay_];
5741 [confirm_ removeFromSuperview];
5748 - (void) setPage:(RVPage *)page {
5749 [page resetViewAnimated:NO];
5750 [page setDelegate:self];
5751 [book_ setPage:page];
5754 - (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
5755 BrowserView *browser = [[[_class alloc] initWithBook:book_ database:database_] autorelease];
5756 [browser loadURL:url];
5760 - (void) _setHomePage {
5761 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
5764 - (void) buttonBarItemTapped:(id)sender {
5765 unsigned tag = [sender tag];
5767 [book_ resetViewAnimated:YES];
5769 } else if (tag_ == 2 && tag != 2)
5770 [install_ resetView];
5773 case 1: [self _setHomePage]; break;
5775 case 2: [self setPage:install_]; break;
5776 case 3: [self setPage:changes_]; break;
5777 case 4: [self setPage:manage_]; break;
5778 case 5: [self setPage:search_]; break;
5780 default: _assert(false);
5786 - (void) fixSpringBoard {
5787 pid_t pid = ExecFork();
5791 if (pid_t child = fork()) {
5792 waitpid(child, NULL, 0);
5794 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5795 perror("launchctl unload");
5799 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5800 perror("launchctl load");
5805 - (void) applicationWillSuspend {
5810 [self fixSpringBoard];
5814 [super applicationWillSuspend];
5817 - (void) askForSettings {
5818 UIAlertSheet *role = [[[UIAlertSheet alloc]
5819 initWithTitle:@"Who Are You?"
5820 buttons:[NSArray arrayWithObjects:
5821 @"User (Graphical Only)",
5822 @"Hacker (+ Command Line)",
5823 @"Developer (No Filters)",
5825 defaultButtonIndex:-1
5830 [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."];
5831 [role popupAlertAnimated:YES];
5836 [self setStatusBarShowsProgress:NO];
5839 [hud_ removeFromSuperview];
5843 pid_t pid = ExecFork();
5845 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5846 perror("launchctl stop");
5853 [self askForSettings];
5857 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5859 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5860 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5861 0, 0, screenrect.size.width, screenrect.size.height - 48
5862 ) database:database_];
5864 [book_ setDelegate:self];
5866 [overlay_ addSubview:book_];
5868 NSArray *buttonitems = [NSArray arrayWithObjects:
5869 [NSDictionary dictionaryWithObjectsAndKeys:
5870 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5871 @"home-up.png", kUIButtonBarButtonInfo,
5872 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5873 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5874 self, kUIButtonBarButtonTarget,
5875 @"Home", kUIButtonBarButtonTitle,
5876 @"0", kUIButtonBarButtonType,
5879 [NSDictionary dictionaryWithObjectsAndKeys:
5880 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5881 @"install-up.png", kUIButtonBarButtonInfo,
5882 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5883 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5884 self, kUIButtonBarButtonTarget,
5885 @"Sections", kUIButtonBarButtonTitle,
5886 @"0", kUIButtonBarButtonType,
5889 [NSDictionary dictionaryWithObjectsAndKeys:
5890 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5891 @"changes-up.png", kUIButtonBarButtonInfo,
5892 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5893 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5894 self, kUIButtonBarButtonTarget,
5895 @"Changes", kUIButtonBarButtonTitle,
5896 @"0", kUIButtonBarButtonType,
5899 [NSDictionary dictionaryWithObjectsAndKeys:
5900 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5901 @"manage-up.png", kUIButtonBarButtonInfo,
5902 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5903 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5904 self, kUIButtonBarButtonTarget,
5905 @"Manage", kUIButtonBarButtonTitle,
5906 @"0", kUIButtonBarButtonType,
5909 [NSDictionary dictionaryWithObjectsAndKeys:
5910 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5911 @"search-up.png", kUIButtonBarButtonInfo,
5912 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5913 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5914 self, kUIButtonBarButtonTarget,
5915 @"Search", kUIButtonBarButtonTitle,
5916 @"0", kUIButtonBarButtonType,
5920 buttonbar_ = [[UIButtonBar alloc]
5922 withFrame:CGRectMake(
5923 0, screenrect.size.height - ButtonBarHeight_,
5924 screenrect.size.width, ButtonBarHeight_
5926 withItemList:buttonitems
5929 [buttonbar_ setDelegate:self];
5930 [buttonbar_ setBarStyle:1];
5931 [buttonbar_ setButtonBarTrackingMode:2];
5933 int buttons[5] = {1, 2, 3, 4, 5};
5934 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5935 [buttonbar_ showButtonGroup:0 withDuration:0];
5937 for (int i = 0; i != 5; ++i)
5938 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5939 i * 64 + 2, 1, 60, ButtonBarHeight_
5942 [buttonbar_ showSelectionForButton:1];
5943 [overlay_ addSubview:buttonbar_];
5945 [UIKeyboard initImplementationNow];
5946 CGSize keysize = [UIKeyboard defaultSize];
5947 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5948 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5949 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5950 [overlay_ addSubview:keyboard_];
5952 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5953 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5954 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5956 manage_ = (ManageView *) [[self
5957 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
5958 withClass:[ManageView class]
5962 [underlay_ addSubview:overlay_];
5969 [self _setHomePage];
5972 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5973 NSString *context = [sheet context];
5974 if ([context isEqualToString:@"fixhalf"])
5977 @synchronized (self) {
5978 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5979 Package *broken = [broken_ objectAtIndex:i];
5982 NSString *id = [broken id];
5983 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5984 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5985 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5986 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5995 [broken_ removeAllObjects];
6002 else if ([context isEqualToString:@"role"]) {
6004 case 1: Role_ = @"User"; break;
6005 case 2: Role_ = @"Hacker"; break;
6006 case 3: Role_ = @"Developer"; break;
6013 bool reset = Settings_ != nil;
6015 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
6019 [Metadata_ setObject:Settings_ forKey:@"Settings"];
6027 } else if ([context isEqualToString:@"upgrade"])
6030 @synchronized (self) {
6031 for (int i = 0, e = [essential_ count]; i != e; ++i) {
6032 Package *essential = [essential_ objectAtIndex:i];
6033 [essential install];
6052 - (void) reorganize {
6053 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6054 system("/usr/libexec/cydia/free.sh");
6055 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
6059 - (void) applicationSuspend:(__GSEvent *)event {
6060 if (hud_ == nil && ![progress_ isRunning])
6061 [super applicationSuspend:event];
6064 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
6066 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
6069 - (void) _setSuspended:(BOOL)value {
6071 [super _setSuspended:value];
6074 - (UIProgressHUD *) addProgressHUD {
6075 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6077 [underlay_ addSubview:hud];
6081 - (void) applicationDidFinishLaunching:(id)unused {
6082 Font12_ = [[UIFont systemFontOfSize:12] retain];
6083 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
6084 Font14_ = [[UIFont systemFontOfSize:14] retain];
6085 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
6086 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
6088 _assert(pkgInitConfig(*_config));
6089 _assert(pkgInitSystem(*_config, _system));
6094 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
6095 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
6097 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
6098 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
6100 [window_ orderFront:self];
6101 [window_ makeKey:self];
6102 [window_ _setHidden:NO];
6104 database_ = [[Database alloc] init];
6105 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
6106 [database_ setDelegate:progress_];
6107 [window_ setContentView:progress_];
6109 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
6110 [progress_ setContentView:underlay_];
6112 [progress_ resetView];
6115 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
6116 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6117 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
6118 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
6119 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
6120 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
6122 [self setIdleTimerDisabled:YES];
6124 hud_ = [self addProgressHUD];
6125 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
6127 [self setStatusBarShowsProgress:YES];
6130 detachNewThreadSelector:@selector(reorganize)
6138 - (void) showKeyboard:(BOOL)show {
6139 CGSize keysize = [UIKeyboard defaultSize];
6140 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
6141 CGRect keyup = keydown;
6142 keyup.origin.y -= keysize.height;
6144 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
6145 [animation setSignificantRectFields:2];
6148 [animation setStartFrame:keydown];
6149 [animation setEndFrame:keyup];
6150 [keyboard_ activate];
6152 [animation setStartFrame:keyup];
6153 [animation setEndFrame:keydown];
6154 [keyboard_ deactivate];
6157 [[UIAnimator sharedAnimator]
6158 addAnimations:[NSArray arrayWithObjects:animation, nil]
6159 withDuration:KeyboardTime_
6164 - (void) slideUp:(UIAlertSheet *)alert {
6166 [alert presentSheetFromButtonBar:buttonbar_];
6168 [alert presentSheetInView:overlay_];
6173 void AddPreferences(NSString *plist) {
6174 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6176 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
6177 _assert(settings != NULL);
6178 NSMutableArray *items = [settings objectForKey:@"items"];
6182 for (size_t i(0); i != [items count]; ++i) {
6183 NSMutableDictionary *item([items objectAtIndex:i]);
6184 NSString *label = [item objectForKey:@"label"];
6185 if (label != nil && [label isEqualToString:@"Cydia"]) {
6192 for (size_t i(0); i != [items count]; ++i) {
6193 NSDictionary *item([items objectAtIndex:i]);
6194 NSString *label = [item objectForKey:@"label"];
6195 if (label != nil && [label isEqualToString:@"General"]) {
6196 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
6197 @"CydiaSettings", @"bundle",
6198 @"PSLinkCell", @"cell",
6199 [NSNumber numberWithBool:YES], @"hasIcon",
6200 [NSNumber numberWithBool:YES], @"isController",
6202 nil] atIndex:(i + 1)];
6208 _assert([settings writeToFile:plist atomically:YES] == YES);
6215 id Alloc_(id self, SEL selector) {
6216 id object = alloc_(self, selector);
6217 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
6222 id Dealloc_(id self, SEL selector) {
6223 id object = dealloc_(self, selector);
6224 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
6228 int main(int argc, char *argv[]) {
6229 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
6231 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
6233 Home_ = NSHomeDirectory();
6236 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
6237 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
6238 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
6239 Sounds_Keyboard_ = [keyboard boolValue];
6245 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
6246 _assert(errno == ENOENT);
6247 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
6248 _assert(errno == ENOENT);
6250 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
6251 alloc_ = alloc->method_imp;
6252 alloc->method_imp = (IMP) &Alloc_;*/
6254 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
6255 dealloc_ = dealloc->method_imp;
6256 dealloc->method_imp = (IMP) &Dealloc_;*/
6258 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
6259 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
6260 Firmware_ = strdup([prover UTF8String]);
6261 NSArray *versions = [prover componentsSeparatedByString:@"."];
6262 int count = [versions count];
6263 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
6264 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
6265 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
6270 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
6271 char *machine = new char[size];
6272 sysctlbyname("hw.machine", machine, &size, NULL, 0);
6275 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
6276 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
6277 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
6278 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
6282 IOObjectRelease(service);
6285 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
6286 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
6288 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
6289 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6291 Settings_ = [Metadata_ objectForKey:@"Settings"];
6293 Packages_ = [Metadata_ objectForKey:@"Packages"];
6294 Sections_ = [Metadata_ objectForKey:@"Sections"];
6295 Sources_ = [Metadata_ objectForKey:@"Sources"];
6298 if (Settings_ != nil)
6299 Role_ = [Settings_ objectForKey:@"Role"];
6301 if (Packages_ == nil) {
6302 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
6303 [Metadata_ setObject:Packages_ forKey:@"Packages"];
6306 if (Sections_ == nil) {
6307 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
6308 [Metadata_ setObject:Sections_ forKey:@"Sections"];
6311 if (Sources_ == nil) {
6312 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
6313 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6316 if (access("/User", F_OK) != 0)
6317 system("/usr/libexec/cydia/firmware.sh");
6319 Locale_ = CFLocaleCopyCurrent();
6320 space_ = CGColorSpaceCreateDeviceRGB();
6322 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
6323 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
6324 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
6325 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
6326 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
6327 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
6328 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
6330 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
6332 int value = UIApplicationMain(argc, argv, [Cydia class]);
6334 CGColorSpaceRelease(space_);