1 /* Cydia - iPhone UIKit Front-End for Debian APT
2 * Copyright (C) 2008 Jay Freeman (saurik)
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 /* #include Directives {{{ */
39 #include <objc/objc.h>
40 #include <objc/runtime.h>
42 #include <CoreGraphics/CoreGraphics.h>
43 #include <GraphicsServices/GraphicsServices.h>
44 #include <Foundation/Foundation.h>
45 #include <UIKit/UIKit.h>
46 #include <WebCore/DOMHTML.h>
48 #import "BrowserView.h"
50 #import "UICaboodle.h"
52 #include <WebKit/WebFrame.h>
53 #include <WebKit/WebView.h>
58 #include <ext/stdio_filebuf.h>
60 #include <apt-pkg/acquire.h>
61 #include <apt-pkg/acquire-item.h>
62 #include <apt-pkg/algorithms.h>
63 #include <apt-pkg/cachefile.h>
64 #include <apt-pkg/clean.h>
65 #include <apt-pkg/configuration.h>
66 #include <apt-pkg/debmetaindex.h>
67 #include <apt-pkg/error.h>
68 #include <apt-pkg/init.h>
69 #include <apt-pkg/pkgrecords.h>
70 #include <apt-pkg/sourcelist.h>
71 #include <apt-pkg/sptr.h>
73 #include <sys/sysctl.h>
77 #include <mach-o/nlist.h>
88 /* iPhoneOS 2.0 Compatibility {{{ */
90 @interface UICGColor : NSObject {
93 - (id) initWithCGColor:(CGColorRef)color;
99 - (UIFont *) fontWithSize:(CGFloat)size;
102 @interface NSObject (iPhoneOS)
103 - (CGColorRef) cgColor;
104 - (CGColorRef) CGColor;
108 @implementation NSObject (iPhoneOS)
110 - (CGColorRef) cgColor {
111 return [self CGColor];
114 - (CGColorRef) CGColor {
115 return (CGColorRef) self;
119 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
124 @interface UITextView (iPhoneOS)
125 - (void) setTextSize:(float)size;
128 @implementation UITextView (iPhoneOS)
130 - (void) setTextSize:(float)size {
131 [self setFont:[[self font] fontWithSize:size]];
140 kUIProgressIndicatorStyleMediumWhite = 1,
141 kUIProgressIndicatorStyleSmallWhite = 0,
142 kUIProgressIndicatorStyleSmallBlack = 4
143 } UIProgressIndicatorStyle;
146 kUIProgressIndicatorStyleMediumWhite = 0,
147 kUIProgressIndicatorStyleSmallWhite = 2,
148 kUIProgressIndicatorStyleSmallBlack = 3
149 } UIProgressIndicatorStyle;
153 kUIControlEventMouseDown = 1 << 0,
154 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
155 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
156 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
157 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
158 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
159 } UIControlEventMasks;
161 @interface NSString (UIKit)
162 - (NSString *) stringByAddingPercentEscapes;
163 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
166 @interface NSString (Cydia)
167 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
168 - (NSComparisonResult) compareByPath:(NSString *)other;
171 @implementation NSString (Cydia)
173 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
174 char data[length + 1];
175 memcpy(data, bytes, length);
177 return [NSString stringWithUTF8String:data];
180 - (NSComparisonResult) compareByPath:(NSString *)other {
181 NSString *prefix = [self commonPrefixWithString:other options:0];
182 size_t length = [prefix length];
184 NSRange lrange = NSMakeRange(length, [self length] - length);
185 NSRange rrange = NSMakeRange(length, [other length] - length);
187 lrange = [self rangeOfString:@"/" options:0 range:lrange];
188 rrange = [other rangeOfString:@"/" options:0 range:rrange];
190 NSComparisonResult value;
192 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
193 value = NSOrderedSame;
194 else if (lrange.location == NSNotFound)
195 value = NSOrderedAscending;
196 else if (rrange.location == NSNotFound)
197 value = NSOrderedDescending;
199 value = NSOrderedSame;
201 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
202 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
203 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
204 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
206 NSComparisonResult result = [lpath compare:rpath];
207 return result == NSOrderedSame ? value : result;
212 /* Perl-Compatible RegEx {{{ */
222 Pcre(const char *regex) :
227 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
230 fprintf(stderr, "%d:%s\n", offset, error);
234 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
235 matches_ = new int[(capture_ + 1) * 3];
243 NSString *operator [](size_t match) {
244 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
247 bool operator ()(NSString *data) {
248 // XXX: length is for characters, not for bytes
249 return operator ()([data UTF8String], [data length]);
252 bool operator ()(const char *data, size_t size) {
254 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
258 /* Mime Addresses {{{ */
259 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
261 @interface Address : NSObject {
267 - (NSString *) email;
269 + (Address *) addressWithString:(NSString *)string;
270 - (Address *) initWithString:(NSString *)string;
273 @implementation Address
282 - (NSString *) name {
286 - (NSString *) email {
290 + (Address *) addressWithString:(NSString *)string {
291 return [[[Address alloc] initWithString:string] autorelease];
294 - (Address *) initWithString:(NSString *)string {
295 if ((self = [super init]) != nil) {
296 const char *data = [string UTF8String];
297 size_t size = [string length];
299 if (email_r(data, size)) {
300 name_ = [email_r[1] retain];
301 email_ = [email_r[2] retain];
303 name_ = [[NSString alloc]
306 encoding:kCFStringEncodingUTF8
316 /* CoreGraphics Primitives {{{ */
327 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
330 Set(space, red, green, blue, alpha);
335 CGColorRelease(color_);
342 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
344 float color[] = {red, green, blue, alpha};
345 color_ = CGColorCreate(space, color);
348 operator CGColorRef() {
363 /* Right Alignment {{{ */
364 @interface UIRightTextLabel : UITextLabel {
365 float _savedRightEdgeX;
366 BOOL _sizedtofit_flag;
369 - (void) setFrame:(CGRect)frame;
370 - (void) setText:(NSString *)text;
371 - (void) realignText;
374 @implementation UIRightTextLabel
376 - (void) setFrame:(CGRect)frame {
377 [super setFrame:frame];
378 if (_sizedtofit_flag == NO) {
379 _savedRightEdgeX = frame.origin.x;
384 - (void) setText:(NSString *)text {
385 [super setText:text];
389 - (void) realignText {
390 CGRect oldFrame = [self frame];
392 _sizedtofit_flag = YES;
393 [self sizeToFit]; // shrink down size so I can right align it
395 CGRect newFrame = [self frame];
397 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
398 oldFrame.size.width = newFrame.size.width;
399 [super setFrame:oldFrame];
400 _sizedtofit_flag = NO;
406 /* Random Global Variables {{{ */
407 static const int PulseInterval_ = 50000;
408 static const int ButtonBarHeight_ = 48;
409 static const float KeyboardTime_ = 0.4f;
410 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
416 static CGColor Blueish_;
417 static CGColor Black_;
418 static CGColor Clear_;
420 static CGColor White_;
422 static NSString *Home_;
423 static BOOL Sounds_Keyboard_;
425 static BOOL Advanced_;
427 static BOOL Ignored_;
429 const char *Firmware_ = NULL;
430 const char *Machine_ = NULL;
431 const char *SerialNumber_ = NULL;
438 CGColorSpaceRef space_;
440 #define FW_LEAST(major, minor, bugfix) \
441 (major < Major_ || major == Major_ && \
442 (minor < Minor_ || minor == Minor_ && \
448 static NSMutableDictionary *Metadata_;
449 static NSMutableDictionary *Packages_;
450 static bool Changed_;
453 NSString *GetLastUpdate() {
454 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
457 return @"Never or Unknown";
459 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
460 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
462 CFRelease(formatter);
464 return [(NSString *) formatted autorelease];
467 /* Display Helpers {{{ */
468 inline float Interpolate(float begin, float end, float fraction) {
469 return (end - begin) * fraction + begin;
472 NSString *SizeString(double size) {
474 while (size > 1024) {
479 static const char *powers_[] = {"B", "kB", "MB", "GB"};
481 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
484 NSString *StripVersion(NSString *version) {
485 NSRange colon = [version rangeOfString:@":"];
486 if (colon.location != NSNotFound)
487 version = [version substringFromIndex:(colon.location + 1)];
491 static const float TextViewOffset_ = 22;
493 UITextView *GetTextView(NSString *value, float left, bool html) {
494 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
495 [text setEditable:NO];
496 [text setTextSize:16];
498 [text setHTML:value];
500 [text setText:value];
501 [text setEnabled:NO];
503 [text setBackgroundColor:Clear_];
505 CGRect frame = [text frame];
506 [text setFrame:frame];
507 CGRect rect = [text visibleTextRect];
508 frame.size.height = rect.size.height;
509 [text setFrame:frame];
514 NSString *Simplify(NSString *title) {
515 const char *data = [title UTF8String];
516 size_t size = [title length];
518 Pcre title_r("^(.*?)( \\(.*\\))?$");
519 if (title_r(data, size))
526 /* Delegate Prototypes {{{ */
530 @interface NSObject (ProgressDelegate)
533 @implementation NSObject(ProgressDelegate)
535 - (void) _setProgressError:(NSArray *)args {
536 [self performSelector:@selector(setProgressError:forPackage:)
537 withObject:[args objectAtIndex:0]
538 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
544 @protocol ProgressDelegate
545 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
546 - (void) setProgressTitle:(NSString *)title;
547 - (void) setProgressPercent:(float)percent;
548 - (void) addProgressOutput:(NSString *)output;
551 @protocol ConfigurationDelegate
552 - (void) repairWithSelector:(SEL)selector;
553 - (void) setConfigurationData:(NSString *)data;
556 @protocol CydiaDelegate
557 - (void) installPackage:(Package *)package;
558 - (void) removePackage:(Package *)package;
559 - (void) slideUp:(UIAlertSheet *)alert;
560 - (void) distUpgrade;
564 /* Status Delegation {{{ */
566 public pkgAcquireStatus
569 _transient NSObject<ProgressDelegate> *delegate_;
577 void setDelegate(id delegate) {
578 delegate_ = delegate;
581 virtual bool MediaChange(std::string media, std::string drive) {
585 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
588 virtual void Fetch(pkgAcquire::ItemDesc &item) {
589 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
592 virtual void Done(pkgAcquire::ItemDesc &item) {
595 virtual void Fail(pkgAcquire::ItemDesc &item) {
597 item.Owner->Status == pkgAcquire::Item::StatIdle ||
598 item.Owner->Status == pkgAcquire::Item::StatDone
602 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
603 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
608 virtual bool Pulse(pkgAcquire *Owner) {
609 bool value = pkgAcquireStatus::Pulse(Owner);
612 double(CurrentBytes + CurrentItems) /
613 double(TotalBytes + TotalItems)
616 [delegate_ setProgressPercent:percent];
620 virtual void Start() {
623 virtual void Stop() {
627 /* Progress Delegation {{{ */
632 _transient id<ProgressDelegate> delegate_;
635 virtual void Update() {
636 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
637 [delegate_ setProgressPercent:(Percent / 100)];
646 void setDelegate(id delegate) {
647 delegate_ = delegate;
650 virtual void Done() {
651 [delegate_ setProgressPercent:1];
656 /* Database Interface {{{ */
657 @interface Database : NSObject {
659 pkgDepCache::Policy *policy_;
660 pkgRecords *records_;
661 pkgProblemResolver *resolver_;
662 pkgAcquire *fetcher_;
664 SPtr<pkgPackageManager> manager_;
665 pkgSourceList *list_;
667 NSMutableDictionary *sources_;
668 NSMutableArray *packages_;
670 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
679 - (void) _readCydia:(NSNumber *)fd;
680 - (void) _readStatus:(NSNumber *)fd;
681 - (void) _readOutput:(NSNumber *)fd;
685 - (Package *) packageWithName:(NSString *)name;
688 - (pkgCacheFile &) cache;
689 - (pkgDepCache::Policy *) policy;
690 - (pkgRecords *) records;
691 - (pkgProblemResolver *) resolver;
692 - (pkgAcquire &) fetcher;
693 - (NSArray *) packages;
702 - (void) updateWithStatus:(Status &)status;
704 - (void) setDelegate:(id)delegate;
705 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
709 /* Source Class {{{ */
710 @interface Source : NSObject {
711 NSString *description_;
716 NSString *distribution_;
720 NSString *defaultIcon_;
725 - (Source *) initWithMetaIndex:(metaIndex *)index;
730 - (NSString *) distribution;
733 - (NSString *) description;
734 - (NSString *) label;
735 - (NSString *) origin;
736 - (NSString *) version;
738 - (NSString *) defaultIcon;
741 @implementation Source
745 [distribution_ release];
748 if (description_ != nil)
749 [description_ release];
756 if (defaultIcon_ != nil)
757 [defaultIcon_ release];
762 - (Source *) initWithMetaIndex:(metaIndex *)index {
763 if ((self = [super init]) != nil) {
764 trusted_ = index->IsTrusted();
766 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
767 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
768 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
776 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
777 if (dindex != NULL) {
778 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
780 while (std::getline(release, line)) {
781 std::string::size_type colon(line.find(':'));
782 if (colon == std::string::npos)
785 std::string name(line.substr(0, colon));
786 std::string value(line.substr(colon + 1));
787 while (!value.empty() && value[0] == ' ')
788 value = value.substr(1);
790 if (name == "Default-Icon")
791 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
792 else if (name == "Description")
793 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
794 else if (name == "Label")
795 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
796 else if (name == "Origin")
797 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
798 else if (name == "Version")
799 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
813 - (NSString *) distribution {
814 return distribution_;
817 - (NSString *) type {
821 - (NSString *) description {
825 - (NSString *) label {
829 - (NSString *) origin {
833 - (NSString *) version {
837 - (NSString *) defaultIcon {
843 /* Relationship Class {{{ */
844 @interface Relationship : NSObject {
855 @implementation Relationship
863 - (NSString *) type {
871 - (NSString *) name {
878 /* Package Class {{{ */
879 NSString *Scour(const char *field, const char *begin, const char *end) {
880 size_t i(0), l(strlen(field));
883 const char *name = begin + i;
884 const char *colon = name + l;
885 const char *value = colon + 1;
890 memcmp(name, field, l) == 0
892 while (value != end && value[0] == ' ')
894 const char *line = std::find(value, end, '\n');
895 while (line != value && line[-1] == ' ')
898 return [NSString stringWithUTF8Bytes:value length:(line - value)];
900 begin = std::find(begin, end, '\n');
908 @interface Package : NSObject {
909 pkgCache::PkgIterator iterator_;
910 _transient Database *database_;
911 pkgCache::VerIterator version_;
912 pkgCache::VerFileIterator file_;
918 NSString *installed_;
928 NSArray *relationships_;
931 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
932 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
934 - (pkgCache::PkgIterator) iterator;
936 - (NSString *) section;
937 - (Address *) maintainer;
939 - (NSString *) description;
940 - (NSString *) index;
944 - (NSString *) latest;
945 - (NSString *) installed;
948 - (BOOL) upgradableAndEssential:(BOOL)essential;
953 - (BOOL) halfConfigured;
954 - (BOOL) halfInstalled;
960 - (NSString *) tagline;
962 - (NSString *) website;
963 - (Address *) author;
965 - (NSArray *) relationships;
969 - (BOOL) matches:(NSString *)text;
971 - (NSComparisonResult) compareByName:(Package *)package;
972 - (NSComparisonResult) compareBySection:(Package *)package;
973 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
974 - (NSComparisonResult) compareForChanges:(Package *)package;
979 - (NSNumber *) isSearchedForBy:(NSString *)search;
980 - (NSNumber *) isInstalledInSection:(NSString *)section;
981 - (NSNumber *) isUninstalledInSection:(NSString *)section;
985 @implementation Package
992 if (installed_ != nil)
993 [installed_ release];
1001 if (website_ != nil)
1003 if (sponsor_ != nil)
1008 if (relationships_ != nil)
1009 [relationships_ release];
1014 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1015 if ((self = [super init]) != nil) {
1016 iterator_ = iterator;
1017 database_ = database;
1019 version_ = [database_ policy]->GetCandidateVer(iterator_);
1020 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1022 if (!version_.end())
1023 file_ = version_.FileList();
1025 pkgCache &cache([database_ cache]);
1026 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1029 pkgCache::VerIterator current = iterator_.CurrentVer();
1030 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1032 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1035 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1037 const char *begin, *end;
1038 parser->GetRec(begin, end);
1040 name_ = Scour("Name", begin, end);
1042 name_ = [name_ retain];
1043 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1044 icon_ = Scour("Icon", begin, end);
1046 icon_ = [icon_ retain];
1047 website_ = Scour("Homepage", begin, end);
1048 if (website_ == nil)
1049 website_ = Scour("Website", begin, end);
1050 if (website_ != nil)
1051 website_ = [website_ retain];
1052 NSString *sponsor = Scour("Sponsor", begin, end);
1054 sponsor_ = [[Address addressWithString:sponsor] retain];
1055 NSString *author = Scour("Author", begin, end);
1057 author_ = [[Address addressWithString:author] retain];
1060 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1061 if (metadata == nil || [metadata count] == 0) {
1062 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1066 [Packages_ setObject:metadata forKey:id_];
1072 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1073 return [[[Package alloc]
1074 initWithIterator:iterator
1079 - (pkgCache::PkgIterator) iterator {
1083 - (NSString *) section {
1084 const char *section = iterator_.Section();
1085 return section == NULL ? nil : [[NSString stringWithUTF8String:section] stringByReplacingCharacter:'_' withCharacter:' '];
1088 - (Address *) maintainer {
1091 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1092 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1096 return version_.end() ? 0 : version_->InstalledSize;
1099 - (NSString *) description {
1102 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1103 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1105 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1106 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1107 if ([lines count] < 2)
1110 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1111 for (size_t i(1); i != [lines count]; ++i) {
1112 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1113 [trimmed addObject:trim];
1116 return [trimmed componentsJoinedByString:@"\n"];
1119 - (NSString *) index {
1120 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1121 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1125 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1128 - (NSString *) latest {
1132 - (NSString *) installed {
1137 return !version_.end();
1140 - (BOOL) upgradableAndEssential:(BOOL)essential {
1141 pkgCache::VerIterator current = iterator_.CurrentVer();
1144 return essential && [self essential];
1146 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1147 return !candidate.end() && candidate != current;
1151 - (BOOL) essential {
1152 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1156 return [database_ cache][iterator_].InstBroken();
1160 unsigned char current = iterator_->CurrentState;
1161 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1164 - (BOOL) halfConfigured {
1165 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1168 - (BOOL) halfInstalled {
1169 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1173 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1174 return state.Mode != pkgDepCache::ModeKeep;
1177 - (NSString *) mode {
1178 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1180 switch (state.Mode) {
1181 case pkgDepCache::ModeDelete:
1182 if ((state.iFlags & pkgDepCache::Purge) != 0)
1187 case pkgDepCache::ModeKeep:
1188 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1193 case pkgDepCache::ModeInstall:
1194 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1195 return @"Reinstall";
1196 else switch (state.Status) {
1198 return @"Downgrade";
1204 return @"New Install";
1217 - (NSString *) name {
1218 return name_ == nil ? id_ : name_;
1221 - (NSString *) tagline {
1225 - (NSString *) icon {
1229 - (NSString *) website {
1233 - (Address *) sponsor {
1237 - (Address *) author {
1241 - (NSArray *) relationships {
1242 return relationships_;
1245 - (Source *) source {
1247 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1254 - (BOOL) matches:(NSString *)text {
1260 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1261 if (range.location != NSNotFound)
1264 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1265 if (range.location != NSNotFound)
1268 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1269 if (range.location != NSNotFound)
1275 - (NSComparisonResult) compareByName:(Package *)package {
1276 NSString *lhs = [self name];
1277 NSString *rhs = [package name];
1279 if ([lhs length] != 0 && [rhs length] != 0) {
1280 unichar lhc = [lhs characterAtIndex:0];
1281 unichar rhc = [rhs characterAtIndex:0];
1283 if (isalpha(lhc) && !isalpha(rhc))
1284 return NSOrderedAscending;
1285 else if (!isalpha(lhc) && isalpha(rhc))
1286 return NSOrderedDescending;
1289 return [lhs caseInsensitiveCompare:rhs];
1292 - (NSComparisonResult) compareBySection:(Package *)package {
1293 NSString *lhs = [self section];
1294 NSString *rhs = [package section];
1296 if (lhs == NULL && rhs != NULL)
1297 return NSOrderedAscending;
1298 else if (lhs != NULL && rhs == NULL)
1299 return NSOrderedDescending;
1300 else if (lhs != NULL && rhs != NULL) {
1301 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1302 if (result != NSOrderedSame)
1306 return NSOrderedSame;
1309 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1310 NSString *lhs = [self section];
1311 NSString *rhs = [package section];
1313 if (lhs == NULL && rhs != NULL)
1314 return NSOrderedAscending;
1315 else if (lhs != NULL && rhs == NULL)
1316 return NSOrderedDescending;
1317 else if (lhs != NULL && rhs != NULL) {
1318 NSComparisonResult result = [lhs compare:rhs];
1319 if (result != NSOrderedSame)
1323 return [self compareByName:package];
1326 - (NSComparisonResult) compareForChanges:(Package *)package {
1327 BOOL lhs = [self upgradableAndEssential:YES];
1328 BOOL rhs = [package upgradableAndEssential:YES];
1331 return lhs ? NSOrderedAscending : NSOrderedDescending;
1333 switch ([[self seen] compare:[package seen]]) {
1334 case NSOrderedAscending:
1335 return NSOrderedDescending;
1338 case NSOrderedDescending:
1339 return NSOrderedAscending;
1345 return [self compareByName:package];
1349 pkgProblemResolver *resolver = [database_ resolver];
1350 resolver->Clear(iterator_);
1351 resolver->Protect(iterator_);
1352 pkgCacheFile &cache([database_ cache]);
1353 cache->MarkInstall(iterator_, false);
1354 pkgDepCache::StateCache &state((*cache)[iterator_]);
1355 if (!state.Install())
1356 cache->SetReInstall(iterator_, true);
1360 pkgProblemResolver *resolver = [database_ resolver];
1361 resolver->Clear(iterator_);
1362 resolver->Protect(iterator_);
1363 resolver->Remove(iterator_);
1364 [database_ cache]->MarkDelete(iterator_, true);
1367 - (NSNumber *) isSearchedForBy:(NSString *)search {
1368 return [NSNumber numberWithBool:([self valid] && [self matches:search])];
1371 - (NSNumber *) isInstalledInSection:(NSString *)section {
1372 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1375 - (NSNumber *) isUninstalledInSection:(NSString *)name {
1376 NSString *section = [self section];
1378 return [NSNumber numberWithBool:([self valid] && [self installed] == nil && (
1380 section == nil && [name length] == 0 ||
1381 [name isEqualToString:section])
1387 /* Section Class {{{ */
1388 @interface Section : NSObject {
1394 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1395 - (NSString *) name;
1398 - (void) addToCount;
1402 @implementation Section
1409 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1410 if ((self = [super init]) != nil) {
1411 name_ = [name retain];
1416 - (NSString *) name {
1428 - (void) addToCount {
1435 /* Database Implementation {{{ */
1436 @implementation Database
1443 - (void) _readCydia:(NSNumber *)fd {
1444 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1446 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1447 std::istream is(&ib);
1450 while (std::getline(is, line)) {
1451 const char *data(line.c_str());
1452 //size_t size = line.size();
1453 fprintf(stderr, "C:%s\n", data);
1460 - (void) _readStatus:(NSNumber *)fd {
1461 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1463 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1464 std::istream is(&ib);
1467 Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1468 Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1470 while (std::getline(is, line)) {
1471 const char *data(line.c_str());
1472 size_t size = line.size();
1473 fprintf(stderr, "S:%s\n", data);
1475 if (conffile_r(data, size)) {
1476 [delegate_ setConfigurationData:conffile_r[1]];
1477 } else if (strncmp(data, "status: ", 8) == 0) {
1478 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1479 [delegate_ setProgressTitle:string];
1480 } else if (pmstatus_r(data, size)) {
1481 std::string type([pmstatus_r[1] UTF8String]);
1482 NSString *id = pmstatus_r[2];
1484 float percent([pmstatus_r[3] floatValue]);
1485 [delegate_ setProgressPercent:(percent / 100)];
1487 NSString *string = pmstatus_r[4];
1489 if (type == "pmerror")
1490 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1491 withObject:[NSArray arrayWithObjects:string, id, nil]
1494 else if (type == "pmstatus")
1495 [delegate_ setProgressTitle:string];
1496 else if (type == "pmconffile")
1497 [delegate_ setConfigurationData:string];
1498 else _assert(false);
1499 } else _assert(false);
1506 - (void) _readOutput:(NSNumber *)fd {
1507 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1509 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1510 std::istream is(&ib);
1513 while (std::getline(is, line)) {
1514 fprintf(stderr, "O:%s\n", line.c_str());
1515 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1526 - (Package *) packageWithName:(NSString *)name {
1527 if (static_cast<pkgDepCache *>(cache_) == NULL)
1529 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1530 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1533 - (Database *) init {
1534 if ((self = [super init]) != nil) {
1541 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1542 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1546 _assert(pipe(fds) != -1);
1549 _config->Set("APT::Keep-Fds::", cydiafd_);
1550 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 0"] UTF8String], _not(int));
1553 detachNewThreadSelector:@selector(_readCydia:)
1555 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1558 _assert(pipe(fds) != -1);
1562 detachNewThreadSelector:@selector(_readStatus:)
1564 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1567 _assert(pipe(fds) != -1);
1568 _assert(dup2(fds[0], 0) != -1);
1569 _assert(close(fds[0]) != -1);
1571 input_ = fdopen(fds[1], "a");
1573 _assert(pipe(fds) != -1);
1574 _assert(dup2(fds[1], 1) != -1);
1575 _assert(close(fds[1]) != -1);
1578 detachNewThreadSelector:@selector(_readOutput:)
1580 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1585 - (pkgCacheFile &) cache {
1589 - (pkgDepCache::Policy *) policy {
1593 - (pkgRecords *) records {
1597 - (pkgProblemResolver *) resolver {
1601 - (pkgAcquire &) fetcher {
1605 - (NSArray *) packages {
1609 - (void) reloadData {
1628 if (!cache_.Open(progress_, true)) {
1630 if (!_error->PopMessage(error))
1633 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1635 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1636 [delegate_ repairWithSelector:@selector(configure)];
1637 else if (error == "The package lists or status file could not be parsed or opened.")
1638 [delegate_ repairWithSelector:@selector(update)];
1639 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1640 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1641 // else if (error == "The list of sources could not be read.")
1642 else _assert(false);
1647 now_ = [[NSDate date] retain];
1649 policy_ = new pkgDepCache::Policy();
1650 records_ = new pkgRecords(cache_);
1651 resolver_ = new pkgProblemResolver(cache_);
1652 fetcher_ = new pkgAcquire(&status_);
1655 list_ = new pkgSourceList();
1656 _assert(list_->ReadMainList());
1658 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1659 _assert(pkgApplyStatus(cache_));
1661 if (cache_->BrokenCount() != 0) {
1662 _assert(pkgFixBroken(cache_));
1663 _assert(cache_->BrokenCount() == 0);
1664 _assert(pkgMinimizeUpgrade(cache_));
1667 [sources_ removeAllObjects];
1668 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1669 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1670 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1672 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1673 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1677 [packages_ removeAllObjects];
1678 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1679 if (Package *package = [Package packageWithIterator:iterator database:self])
1680 [packages_ addObject:package];
1682 [packages_ sortUsingSelector:@selector(compareByName:)];
1685 - (void) configure {
1686 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1687 system([dpkg UTF8String]);
1695 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1696 _assert(!_error->PendingError());
1699 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1702 public pkgArchiveCleaner
1705 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1710 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1712 while (_error->PopMessage(error))
1713 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1718 pkgRecords records(cache_);
1720 lock_ = new FileFd();
1721 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1722 _assert(!_error->PendingError());
1725 // XXX: explain this with an error message
1726 _assert(list.ReadMainList());
1728 manager_ = (_system->CreatePM(cache_));
1729 _assert(manager_->GetArchives(fetcher_, &list, &records));
1730 _assert(!_error->PendingError());
1734 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1736 _assert(list.ReadMainList());
1737 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1738 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1741 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1745 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1747 if (result == pkgPackageManager::Failed)
1749 if (_error->PendingError())
1751 if (result != pkgPackageManager::Completed)
1754 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1756 _assert(list.ReadMainList());
1757 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1758 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1761 if (![before isEqualToArray:after])
1766 _assert(pkgDistUpgrade(cache_));
1770 [self updateWithStatus:status_];
1773 - (void) updateWithStatus:(Status &)status {
1775 _assert(list.ReadMainList());
1778 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1779 _assert(!_error->PendingError());
1781 pkgAcquire fetcher(&status);
1782 _assert(list.GetIndexes(&fetcher));
1784 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1785 bool failed = false;
1786 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1787 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1788 (*item)->Finished();
1792 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1793 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1794 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1797 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1802 - (void) setDelegate:(id)delegate {
1803 delegate_ = delegate;
1804 status_.setDelegate(delegate);
1805 progress_.setDelegate(delegate);
1808 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1809 pkgIndexFile *index(NULL);
1810 list_->FindIndex(file, index);
1811 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1817 /* Confirmation View {{{ */
1818 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1819 if ([packages count] == 0)
1822 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1823 [fields setObject:text forKey:key];
1825 CGColor blue(space_, 0, 0, 0.4, 1);
1826 [text setTextColor:blue];
1829 @protocol ConfirmationViewDelegate
1834 @interface ConfirmationView : UIView {
1835 Database *database_;
1837 UITransitionView *transition_;
1839 UINavigationBar *navbar_;
1840 UIPreferencesTable *table_;
1841 NSMutableDictionary *fields_;
1842 UIAlertSheet *essential_;
1847 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1851 @implementation ConfirmationView
1854 [navbar_ setDelegate:nil];
1855 [transition_ setDelegate:nil];
1856 [table_ setDataSource:nil];
1858 [transition_ release];
1863 if (essential_ != nil)
1864 [essential_ release];
1869 [transition_ transition:7 toView:nil];
1873 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1874 if (from != nil && to == nil)
1875 [self removeFromSuperview];
1878 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1881 if (essential_ != nil)
1882 [essential_ popupAlertAnimated:YES];
1884 [delegate_ confirm];
1893 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1894 NSString *context = [sheet context];
1896 if ([context isEqualToString:@"remove"])
1902 [delegate_ confirm];
1907 else if ([context isEqualToString:@"unable"])
1913 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1917 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1919 case 0: return @"Statistics";
1920 case 1: return @"Modifications";
1922 default: _assert(false);
1926 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1929 case 1: return [fields_ count];
1931 default: _assert(false);
1935 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1936 if (group != 1 || row == -1)
1939 _assert(size_t(row) < [fields_ count]);
1940 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1944 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1945 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1946 [cell setShowSelection:NO];
1949 case 0: switch (row) {
1951 [cell setTitle:@"Downloading"];
1952 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1956 [cell setTitle:@"Resuming At"];
1957 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1961 double size([database_ cache]->UsrSize());
1964 [cell setTitle:@"Disk Freeing"];
1965 [cell setValue:SizeString(-size)];
1967 [cell setTitle:@"Disk Using"];
1968 [cell setValue:SizeString(size)];
1972 default: _assert(false);
1976 _assert(size_t(row) < [fields_ count]);
1977 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1978 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1981 default: _assert(false);
1987 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1988 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1989 database_ = database;
1990 delegate_ = delegate;
1992 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1993 [self addSubview:transition_];
1995 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1997 CGSize navsize = [UINavigationBar defaultSize];
1998 CGRect navrect = {{0, 0}, navsize};
1999 CGRect bounds = [overlay_ bounds];
2001 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2003 [navbar_ setBarStyle:1];
2004 [navbar_ setDelegate:self];
2006 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2007 [navbar_ pushNavigationItem:navitem];
2008 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2010 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2012 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2013 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2014 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2015 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2016 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2020 pkgCacheFile &cache([database_ cache]);
2021 NSArray *packages = [database_ packages];
2022 for (size_t i(0), e = [packages count]; i != e; ++i) {
2023 Package *package = [packages objectAtIndex:i];
2024 pkgCache::PkgIterator iterator = [package iterator];
2025 pkgDepCache::StateCache &state(cache[iterator]);
2027 NSString *name([package name]);
2029 if (state.NewInstall())
2030 [installing addObject:name];
2031 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2032 [reinstalling addObject:name];
2033 else if (state.Upgrade())
2034 [upgrading addObject:name];
2035 else if (state.Downgrade())
2036 [downgrading addObject:name];
2037 else if (state.Delete()) {
2038 if ([package essential])
2040 [removing addObject:name];
2046 else if (Advanced_ || true) {
2047 essential_ = [[UIAlertSheet alloc]
2048 initWithTitle:@"Removing Essentials"
2049 buttons:[NSArray arrayWithObjects:
2050 @"Cancel Operation (Safe)",
2051 @"Force Removal (Unsafe)",
2053 defaultButtonIndex:0
2059 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2061 [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."];
2063 essential_ = [[UIAlertSheet alloc]
2064 initWithTitle:@"Unable to Comply"
2065 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2066 defaultButtonIndex:0
2071 [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."];
2074 AddTextView(fields_, installing, @"Installing");
2075 AddTextView(fields_, reinstalling, @"Reinstalling");
2076 AddTextView(fields_, upgrading, @"Upgrading");
2077 AddTextView(fields_, downgrading, @"Downgrading");
2078 AddTextView(fields_, removing, @"Removing");
2080 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2081 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2084 [table_ setReusesTableCells:YES];
2085 [table_ setDataSource:self];
2086 [table_ reloadData];
2088 [overlay_ addSubview:navbar_];
2089 [overlay_ addSubview:table_];
2091 [view addSubview:self];
2093 [transition_ setDelegate:self];
2095 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2096 [transition_ transition:0 toView:blank];
2097 [transition_ transition:3 toView:overlay_];
2104 /* Progress Data {{{ */
2105 @interface ProgressData : NSObject {
2111 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2118 @implementation ProgressData
2120 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2121 if ((self = [super init]) != nil) {
2122 selector_ = selector;
2142 /* Progress View {{{ */
2143 Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2145 @interface ProgressView : UIView <
2146 ConfigurationDelegate,
2149 _transient Database *database_;
2151 UIView *background_;
2152 UITransitionView *transition_;
2154 UINavigationBar *navbar_;
2155 UIProgressBar *progress_;
2156 UITextView *output_;
2157 UITextLabel *status_;
2158 UIPushButton *close_;
2162 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2164 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2165 - (void) setContentView:(UIView *)view;
2168 - (void) _retachThread;
2169 - (void) _detachNewThreadData:(ProgressData *)data;
2170 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2174 @protocol ProgressViewDelegate
2175 - (void) progressViewIsComplete:(ProgressView *)sender;
2178 @implementation ProgressView
2181 [transition_ setDelegate:nil];
2182 [navbar_ setDelegate:nil];
2185 if (background_ != nil)
2186 [background_ release];
2187 [transition_ release];
2190 [progress_ release];
2197 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2198 if (bootstrap_ && from == overlay_ && to == view_)
2202 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2203 if ((self = [super initWithFrame:frame]) != nil) {
2204 database_ = database;
2205 delegate_ = delegate;
2207 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2208 [transition_ setDelegate:self];
2210 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2213 [overlay_ setBackgroundColor:Black_];
2215 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2216 [background_ setBackgroundColor:Black_];
2217 [self addSubview:background_];
2220 [self addSubview:transition_];
2222 CGSize navsize = [UINavigationBar defaultSize];
2223 CGRect navrect = {{0, 0}, navsize};
2225 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2226 [overlay_ addSubview:navbar_];
2228 [navbar_ setBarStyle:1];
2229 [navbar_ setDelegate:self];
2231 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2232 [navbar_ pushNavigationItem:navitem];
2234 CGRect bounds = [overlay_ bounds];
2235 CGSize prgsize = [UIProgressBar defaultSize];
2238 (bounds.size.width - prgsize.width) / 2,
2239 bounds.size.height - prgsize.height - 20
2242 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2243 [progress_ setStyle:0];
2245 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2247 bounds.size.height - prgsize.height - 50,
2248 bounds.size.width - 20,
2252 [status_ setColor:White_];
2253 [status_ setBackgroundColor:Clear_];
2255 [status_ setCentersHorizontally:YES];
2256 //[status_ setFont:font];
2258 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2260 navrect.size.height + 20,
2261 bounds.size.width - 20,
2262 bounds.size.height - navsize.height - 62 - navrect.size.height
2265 //[output_ setTextFont:@"Courier New"];
2266 [output_ setTextSize:12];
2268 [output_ setTextColor:White_];
2269 [output_ setBackgroundColor:Clear_];
2271 [output_ setMarginTop:0];
2272 [output_ setAllowsRubberBanding:YES];
2273 [output_ setEditable:NO];
2275 [overlay_ addSubview:output_];
2277 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2279 bounds.size.height - prgsize.height - 50,
2280 bounds.size.width - 20,
2284 [close_ setAutosizesToFit:NO];
2285 [close_ setDrawsShadow:YES];
2286 [close_ setStretchBackground:YES];
2287 [close_ setTitle:@"Close Window"];
2288 [close_ setEnabled:YES];
2290 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2291 [close_ setTitleFont:bold];
2294 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2295 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2296 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2300 - (void) setContentView:(UIView *)view {
2301 view_ = [view retain];
2304 - (void) resetView {
2305 [transition_ transition:6 toView:view_];
2308 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2309 NSString *context = [sheet context];
2310 if ([context isEqualToString:@"conffile"]) {
2311 FILE *input = [database_ input];
2315 fprintf(input, "N\n");
2319 fprintf(input, "Y\n");
2330 - (void) closeButtonPushed {
2331 [delegate_ progressViewIsComplete:self];
2335 - (void) _retachThread {
2336 UINavigationItem *item = [navbar_ topItem];
2337 [item setTitle:@"Complete"];
2339 [overlay_ addSubview:close_];
2340 [progress_ removeFromSuperview];
2341 [status_ removeFromSuperview];
2344 - (void) _detachNewThreadData:(ProgressData *)data {
2345 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2347 [[data target] performSelector:[data selector] withObject:[data object]];
2350 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2355 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2356 UINavigationItem *item = [navbar_ topItem];
2357 [item setTitle:title];
2359 [status_ setText:nil];
2360 [output_ setText:@""];
2361 [progress_ setProgress:0];
2363 [close_ removeFromSuperview];
2364 [overlay_ addSubview:progress_];
2365 [overlay_ addSubview:status_];
2367 [transition_ transition:6 toView:overlay_];
2370 detachNewThreadSelector:@selector(_detachNewThreadData:)
2372 withObject:[[ProgressData alloc]
2373 initWithSelector:selector
2380 - (void) repairWithSelector:(SEL)selector {
2382 detachNewThreadSelector:selector
2385 title:@"Repairing..."
2389 - (void) setConfigurationData:(NSString *)data {
2391 performSelectorOnMainThread:@selector(_setConfigurationData:)
2397 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2398 Package *package = id == nil ? nil : [database_ packageWithName:id];
2400 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2401 initWithTitle:(package == nil ? @"Source Error" : [package name])
2402 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2403 defaultButtonIndex:0
2408 [sheet setBodyText:error];
2409 [sheet popupAlertAnimated:YES];
2412 - (void) setProgressTitle:(NSString *)title {
2414 performSelectorOnMainThread:@selector(_setProgressTitle:)
2420 - (void) setProgressPercent:(float)percent {
2422 performSelectorOnMainThread:@selector(_setProgressPercent:)
2423 withObject:[NSNumber numberWithFloat:percent]
2428 - (void) addProgressOutput:(NSString *)output {
2430 performSelectorOnMainThread:@selector(_addProgressOutput:)
2436 - (void) _setConfigurationData:(NSString *)data {
2437 _assert(conffile_r(data));
2439 NSString *ofile = conffile_r[1];
2440 //NSString *nfile = conffile_r[2];
2442 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2443 initWithTitle:@"Configuration Upgrade"
2444 buttons:[NSArray arrayWithObjects:
2445 @"Keep My Old Copy",
2446 @"Accept The New Copy",
2447 // XXX: @"See What Changed",
2449 defaultButtonIndex:0
2454 [sheet setBodyText:[NSString stringWithFormat:
2455 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2458 [sheet popupAlertAnimated:YES];
2461 - (void) _setProgressTitle:(NSString *)title {
2462 [status_ setText:[title stringByAppendingString:@"..."]];
2465 - (void) _setProgressPercent:(NSNumber *)percent {
2466 [progress_ setProgress:[percent floatValue]];
2469 - (void) _addProgressOutput:(NSString *)output {
2470 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2471 CGSize size = [output_ contentSize];
2472 CGRect rect = {{0, size.height}, {size.width, 0}};
2473 [output_ scrollRectToVisible:rect animated:YES];
2479 /* Package Cell {{{ */
2480 @interface PackageCell : UITableCell {
2483 UITextLabel *description_;
2484 UITextLabel *source_;
2485 //UIImageView *trusted_;
2487 UIImageView *badge_;
2488 UITextLabel *status_;
2493 - (PackageCell *) init;
2494 - (void) setPackage:(Package *)package;
2496 - (void) _setSelected:(float)fraction;
2497 - (void) setSelected:(BOOL)selected;
2498 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2499 - (void) _setSelectionFadeFraction:(float)fraction;
2501 + (int) heightForPackage:(Package *)package;
2505 @implementation PackageCell
2510 [description_ release];
2516 //[trusted_ release];
2520 - (PackageCell *) init {
2521 if ((self = [super init]) != nil) {
2522 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2523 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2524 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2526 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2528 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2529 [name_ setBackgroundColor:Clear_];
2530 [name_ setFont:bold];
2532 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2533 [source_ setBackgroundColor:Clear_];
2534 [source_ setFont:large];
2536 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2537 [description_ setBackgroundColor:Clear_];
2538 [description_ setFont:small];
2540 /*trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2541 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];*/
2544 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2546 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2547 [status_ setBackgroundColor:Clear_];
2548 [status_ setFont:small];
2551 /*[icon_ setImage:[UIImage applicationImageNamed:@"unknown.png"]];
2552 [icon_ zoomToScale:0.5];
2553 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];*/
2555 [self addSubview:icon_];
2556 [self addSubview:name_];
2557 [self addSubview:description_];
2558 [self addSubview:source_];
2566 - (void) setPackage:(Package *)package {
2572 Source *source = [package source];
2574 UIImage *image = nil;
2575 if (NSString *icon = [package icon])
2576 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2577 if (image == nil) if (NSString *icon = [source defaultIcon])
2578 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2580 image = [UIImage applicationImageNamed:@"unknown.png"];
2581 [icon_ setImage:image];
2583 /*if (image != nil) {
2584 CGSize size = [image size];
2585 float scale = 30 / std::max(size.width, size.height);
2586 [icon_ zoomToScale:scale];
2589 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2591 [name_ setText:[package name]];
2592 [description_ setText:[package tagline]];
2594 NSString *label = nil;
2595 bool trusted = false;
2597 if (source != nil) {
2598 label = [source label];
2599 trusted = [source trusted];
2600 } else if ([[package id] isEqualToString:@"firmware"])
2604 label = @"Unknown/Local";
2606 NSString *from = [NSString stringWithFormat:@"from %@", label];
2608 NSString *section = Simplify([package section]);
2609 if (section != nil && ![section isEqualToString:label])
2610 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2612 [source_ setText:from];
2615 [badge_ removeFromSuperview];
2616 [status_ removeFromSuperview];
2618 if (NSString *mode = [package mode]) {
2619 [badge_ setImage:[UIImage applicationImageNamed:
2620 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2623 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2624 [status_ setColor:Blueish_];
2625 } else if ([package half]) {
2626 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2627 [status_ setText:@"Package Damaged"];
2628 [status_ setColor:Red_];
2630 [badge_ setImage:nil];
2631 [status_ setText:nil];
2635 [self addSubview:badge_];
2636 [self addSubview:status_];
2641 - (void) _setSelected:(float)fraction {
2642 CGColor black(space_,
2643 Interpolate(0.0, 1.0, fraction),
2644 Interpolate(0.0, 1.0, fraction),
2645 Interpolate(0.0, 1.0, fraction),
2648 CGColor gray(space_,
2649 Interpolate(0.4, 1.0, fraction),
2650 Interpolate(0.4, 1.0, fraction),
2651 Interpolate(0.4, 1.0, fraction),
2654 [name_ setColor:black];
2655 [description_ setColor:gray];
2656 [source_ setColor:black];
2659 - (void) setSelected:(BOOL)selected {
2660 [self _setSelected:(selected ? 1.0 : 0.0)];
2661 [super setSelected:selected];
2664 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2666 [self _setSelected:(selected ? 1.0 : 0.0)];
2667 [super setSelected:selected withFade:fade];
2670 - (void) _setSelectionFadeFraction:(float)fraction {
2671 [self _setSelected:fraction];
2672 [super _setSelectionFadeFraction:fraction];
2675 + (int) heightForPackage:(Package *)package {
2677 if ([package hasMode] || [package half])
2686 /* Section Cell {{{ */
2687 @interface SectionCell : UITableCell {
2689 UITextLabel *count_;
2693 - (void) setSection:(Section *)section;
2695 - (void) _setSelected:(float)fraction;
2696 - (void) setSelected:(BOOL)selected;
2697 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2698 - (void) _setSelectionFadeFraction:(float)fraction;
2702 @implementation SectionCell
2711 if ((self = [super init]) != nil) {
2712 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2713 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2715 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2716 [name_ setBackgroundColor:Clear_];
2717 [name_ setFont:bold];
2719 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2720 [count_ setCentersHorizontally:YES];
2721 [count_ setBackgroundColor:Clear_];
2722 [count_ setFont:small];
2723 [count_ setColor:White_];
2725 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2726 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2728 [self addSubview:folder];
2729 [self addSubview:name_];
2730 [self addSubview:count_];
2732 [self _setSelected:0];
2739 - (void) setSection:(Section *)section {
2740 if (section == nil) {
2741 [name_ setText:@"All Packages"];
2742 [count_ setText:nil];
2744 NSString *name = [section name];
2745 [name_ setText:(name == nil ? @"(No Section)" : name)];
2746 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2750 - (void) _setSelected:(float)fraction {
2751 CGColor black(space_,
2752 Interpolate(0.0, 1.0, fraction),
2753 Interpolate(0.0, 1.0, fraction),
2754 Interpolate(0.0, 1.0, fraction),
2757 [name_ setColor:black];
2760 - (void) setSelected:(BOOL)selected {
2761 [self _setSelected:(selected ? 1.0 : 0.0)];
2762 [super setSelected:selected];
2765 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2767 [self _setSelected:(selected ? 1.0 : 0.0)];
2768 [super setSelected:selected withFade:fade];
2771 - (void) _setSelectionFadeFraction:(float)fraction {
2772 [self _setSelected:fraction];
2773 [super _setSelectionFadeFraction:fraction];
2779 /* File Table {{{ */
2780 @interface FileTable : RVPage {
2781 _transient Database *database_;
2784 NSMutableArray *files_;
2788 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2789 - (void) setPackage:(Package *)package;
2793 @implementation FileTable
2796 if (package_ != nil)
2805 - (int) numberOfRowsInTable:(UITable *)table {
2806 return files_ == nil ? 0 : [files_ count];
2809 - (float) table:(UITable *)table heightForRow:(int)row {
2813 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2814 if (reusing == nil) {
2815 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
2816 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2817 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
2820 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
2824 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
2828 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2829 if ((self = [super initWithBook:book]) != nil) {
2830 database_ = database;
2832 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
2834 list_ = [[UITable alloc] initWithFrame:[self bounds]];
2835 [self addSubview:list_];
2837 UITableColumn *column = [[[UITableColumn alloc]
2838 initWithTitle:@"Name"
2840 width:[self frame].size.width
2843 [list_ setDataSource:self];
2844 [list_ setSeparatorStyle:1];
2845 [list_ addTableColumn:column];
2846 [list_ setDelegate:self];
2847 [list_ setReusesTableCells:YES];
2851 - (void) setPackage:(Package *)package {
2852 if (package_ != nil) {
2853 [package_ autorelease];
2862 [files_ removeAllObjects];
2864 if (package != nil) {
2865 package_ = [package retain];
2866 name_ = [[package id] retain];
2868 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
2871 std::ifstream fin([path UTF8String]);
2873 while (std::getline(fin, line))
2874 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
2877 if ([files_ count] != 0) {
2878 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
2879 [files_ removeObjectAtIndex:0];
2880 [files_ sortUsingSelector:@selector(compareByPath:)];
2882 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
2883 [stack addObject:@"/"];
2885 for (int i(0), e([files_ count]); i != e; ++i) {
2886 NSString *file = [files_ objectAtIndex:i];
2887 while (![file hasPrefix:[stack lastObject]])
2888 [stack removeLastObject];
2889 NSString *directory = [stack lastObject];
2890 [stack addObject:[file stringByAppendingString:@"/"]];
2891 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
2892 ([stack count] - 2) * 3, "",
2893 [file substringFromIndex:[directory length]]
2902 - (void) resetViewAnimated:(BOOL)animated {
2903 [list_ resetViewAnimated:animated];
2906 - (void) reloadData {
2907 [self setPackage:[database_ packageWithName:name_]];
2908 [self reloadButtons];
2911 - (NSString *) title {
2912 return @"Installed Files";
2915 - (NSString *) backButtonTitle {
2921 /* Package View {{{ */
2922 @protocol PackageViewDelegate
2923 - (void) performPackage:(Package *)package;
2926 @interface PackageView : RVPage {
2927 _transient Database *database_;
2928 UIPreferencesTable *table_;
2931 UITextView *description_;
2932 NSMutableArray *buttons_;
2935 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2936 - (void) setPackage:(Package *)package;
2940 @implementation PackageView
2943 [table_ setDataSource:nil];
2944 [table_ setDelegate:nil];
2946 if (package_ != nil)
2950 if (description_ != nil)
2951 [description_ release];
2957 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2959 if ([package_ installed] != nil)
2961 if ([package_ source] != nil)
2966 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2969 else if ([package_ installed] != nil && group-- == 0)
2970 return @"Installed Package";
2971 else if (group-- == 0)
2972 return @"Package Details";
2973 else if ([package_ source] != nil && group-- == 0)
2974 return @"Source Information";
2975 else _assert(false);
2978 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2979 if (description_ == nil || group != 0 || row != 1)
2982 return [description_ visibleTextRect].size.height + TextViewOffset_;
2985 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2988 if ([package_ author] != nil)
2990 if (description_ != nil)
2992 if ([package_ website] != nil)
2995 } else if ([package_ installed] != nil && group-- == 0)
2997 else if (group-- == 0) {
2999 if ([package_ size] != 0)
3001 if ([package_ maintainer] != nil)
3003 if ([package_ sponsor] != nil)
3005 if ([package_ relationships] != nil)
3007 if ([[package_ source] trusted])
3010 } else if ([package_ source] != nil && group-- == 0) {
3011 Source *source = [package_ source];
3012 NSString *description = [source description];
3014 if (description != nil && ![description isEqualToString:[source label]])
3016 if ([source origin] != nil)
3019 } else _assert(false);
3022 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3023 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3024 [cell setShowSelection:NO];
3028 } else if (row-- == 0) {
3029 [cell setTitle:[package_ name]];
3030 [cell setValue:[package_ latest]];
3031 } else if ([package_ author] != nil && row-- == 0) {
3032 [cell setTitle:@"Author"];
3033 [cell setValue:[[package_ author] name]];
3034 [cell setShowDisclosure:YES];
3035 [cell setShowSelection:YES];
3036 } else if (description_ != nil && row-- == 0) {
3037 [cell addSubview:description_];
3038 } else if ([package_ website] != nil && row-- == 0) {
3039 [cell setTitle:@"More Information"];
3040 [cell setShowDisclosure:YES];
3041 [cell setShowSelection:YES];
3042 } else _assert(false);
3043 } else if ([package_ installed] != nil && group-- == 0) {
3045 } else if (row-- == 0) {
3046 [cell setTitle:@"Version"];
3047 NSString *installed([package_ installed]);
3048 [cell setValue:(installed == nil ? @"n/a" : installed)];
3049 } else if (row-- == 0) {
3050 [cell setTitle:@"Filesystem Content"];
3051 [cell setShowDisclosure:YES];
3052 [cell setShowSelection:YES];
3053 } else _assert(false);
3054 } else if (group-- == 0) {
3056 } else if (row-- == 0) {
3057 [cell setTitle:@"Identifier"];
3058 [cell setValue:[package_ id]];
3059 } else if (row-- == 0) {
3060 [cell setTitle:@"Section"];
3061 NSString *section([package_ section]);
3062 [cell setValue:(section == nil ? @"n/a" : section)];
3063 } else if ([package_ size] != 0 && row-- == 0) {
3064 [cell setTitle:@"Expanded Size"];
3065 [cell setValue:SizeString([package_ size])];
3066 } else if ([package_ maintainer] != nil && row-- == 0) {
3067 [cell setTitle:@"Maintainer"];
3068 [cell setValue:[[package_ maintainer] name]];
3069 [cell setShowDisclosure:YES];
3070 [cell setShowSelection:YES];
3071 } else if ([package_ sponsor] != nil && row-- == 0) {
3072 [cell setTitle:@"Sponsor"];
3073 [cell setValue:[[package_ sponsor] name]];
3074 [cell setShowDisclosure:YES];
3075 [cell setShowSelection:YES];
3076 } else if ([package_ relationships] != nil && row-- == 0) {
3077 [cell setTitle:@"Package Relationships"];
3078 [cell setShowDisclosure:YES];
3079 [cell setShowSelection:YES];
3080 } else if ([[package_ source] trusted] && row-- == 0) {
3081 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3082 [cell setValue:@"This package has been signed."];
3083 } else _assert(false);
3084 } else if ([package_ source] != nil && group-- == 0) {
3085 Source *source = [package_ source];
3086 NSString *description = [source description];
3089 } else if (row-- == 0) {
3090 NSString *label = [source label];
3092 label = [source uri];
3093 [cell setTitle:label];
3094 [cell setValue:[source version]];
3095 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3096 [cell setValue:description];
3097 } else if ([source origin] != nil && row-- == 0) {
3098 [cell setTitle:@"Origin"];
3099 [cell setValue:[source origin]];
3100 } else _assert(false);
3101 } else _assert(false);
3106 - (BOOL) canSelectRow:(int)row {
3110 - (void) tableRowSelected:(NSNotification *)notification {
3111 int row = [table_ selectedRow];
3115 #define _else else goto _label; return; } _label:
3119 } else if (row-- == 0) {
3120 } else if ([package_ author] != nil && row-- == 0) {
3121 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3122 [[package_ author] email],
3123 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3125 ] stringByAddingPercentEscapes]
3127 } else if (description_ != nil && row-- == 0) {
3128 } else if ([package_ website] != nil && row-- == 0) {
3129 NSURL *url = [NSURL URLWithString:[package_ website]];
3130 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3131 [browser setDelegate:delegate_];
3132 [book_ pushPage:browser];
3133 [browser loadURL:url];
3134 } _else if ([package_ installed] != nil) {
3136 } else if (row-- == 0) {
3137 } else if (row-- == 0) {
3138 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3139 [files setDelegate:delegate_];
3140 [files setPackage:package_];
3141 [book_ pushPage:files];
3144 } else if (row-- == 0) {
3145 } else if (row-- == 0) {
3146 } else if ([package_ size] != 0 && row-- == 0) {
3147 } else if ([package_ maintainer] != nil && row-- == 0) {
3148 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3149 [[package_ maintainer] email],
3150 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3152 ] stringByAddingPercentEscapes]
3154 } else if ([package_ sponsor] != nil && row-- == 0) {
3155 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3156 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3157 [browser setDelegate:delegate_];
3158 [book_ pushPage:browser];
3159 [browser loadURL:url];
3160 } else if ([package_ relationships] != nil && row-- == 0) {
3161 } else if ([[package_ source] trusted] && row-- == 0) {
3162 } _else if ([package_ source] != nil) {
3163 Source *source = [package_ source];
3164 NSString *description = [source description];
3167 } else if (row-- == 0) {
3168 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3169 } else if ([source origin] != nil && row-- == 0) {
3170 } _else _assert(false);
3175 - (void) _clickButtonWithName:(NSString *)name {
3176 if ([name isEqualToString:@"Install"])
3177 [delegate_ installPackage:package_];
3178 else if ([name isEqualToString:@"Reinstall"])
3179 [delegate_ installPackage:package_];
3180 else if ([name isEqualToString:@"Remove"])
3181 [delegate_ removePackage:package_];
3182 else if ([name isEqualToString:@"Upgrade"])
3183 [delegate_ installPackage:package_];
3184 else _assert(false);
3187 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3188 int count = [buttons_ count];
3189 _assert(count != 0);
3190 _assert(button <= count + 1);
3192 if (count != button - 1)
3193 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3198 - (void) _rightButtonClicked {
3199 int count = [buttons_ count];
3200 _assert(count != 0);
3203 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3205 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3206 [buttons addObjectsFromArray:buttons_];
3207 [buttons addObject:@"Cancel"];
3209 [delegate_ slideUp:[[[UIAlertSheet alloc]
3212 defaultButtonIndex:2
3219 - (NSString *) rightButtonTitle {
3220 int count = [buttons_ count];
3221 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3224 - (NSString *) title {
3228 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3229 if ((self = [super initWithBook:book]) != nil) {
3230 database_ = database;
3232 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3233 [self addSubview:table_];
3235 [table_ setDataSource:self];
3236 [table_ setDelegate:self];
3238 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3242 - (void) setPackage:(Package *)package {
3243 if (package_ != nil) {
3244 [package_ autorelease];
3253 if (description_ != nil) {
3254 [description_ release];
3258 [buttons_ removeAllObjects];
3260 if (package != nil) {
3261 package_ = [package retain];
3262 name_ = [[package id] retain];
3264 NSString *description([package description]);
3265 if (description == nil)
3266 description = [package tagline];
3267 if (description != nil) {
3268 description_ = [GetTextView(description, 12, true) retain];
3269 [description_ setTextColor:Black_];
3272 [table_ reloadData];
3274 if ([package_ source] == nil);
3275 else if ([package_ upgradableAndEssential:NO])
3276 [buttons_ addObject:@"Upgrade"];
3277 else if ([package_ installed] == nil)
3278 [buttons_ addObject:@"Install"];
3280 [buttons_ addObject:@"Reinstall"];
3281 if ([package_ installed] != nil)
3282 [buttons_ addObject:@"Remove"];
3286 - (void) resetViewAnimated:(BOOL)animated {
3287 [table_ resetViewAnimated:animated];
3290 - (void) reloadData {
3291 [self setPackage:[database_ packageWithName:name_]];
3292 [self reloadButtons];
3297 /* Package Table {{{ */
3298 @interface PackageTable : RVPage {
3299 _transient Database *database_;
3303 NSMutableArray *packages_;
3304 NSMutableArray *sections_;
3305 UISectionList *list_;
3308 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3310 - (void) setDelegate:(id)delegate;
3311 - (void) setObject:(id)object;
3313 - (void) reloadData;
3314 - (void) resetCursor;
3316 - (UISectionList *) list;
3318 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3322 @implementation PackageTable
3325 [list_ setDataSource:nil];
3330 [packages_ release];
3331 [sections_ release];
3336 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3337 return [sections_ count];
3340 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3341 return [[sections_ objectAtIndex:section] name];
3344 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3345 return [[sections_ objectAtIndex:section] row];
3348 - (int) numberOfRowsInTable:(UITable *)table {
3349 return [packages_ count];
3352 - (float) table:(UITable *)table heightForRow:(int)row {
3353 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3356 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3358 reusing = [[[PackageCell alloc] init] autorelease];
3359 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3363 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3367 - (void) tableRowSelected:(NSNotification *)notification {
3368 int row = [[notification object] selectedRow];
3372 Package *package = [packages_ objectAtIndex:row];
3373 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3374 [view setDelegate:delegate_];
3375 [view setPackage:package];
3376 [book_ pushPage:view];
3379 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3380 if ((self = [super initWithBook:book]) != nil) {
3381 database_ = database;
3382 title_ = [title retain];
3384 object_ = object == nil ? nil : [object retain];
3386 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3387 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3389 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3390 [list_ setDataSource:self];
3392 UITableColumn *column = [[[UITableColumn alloc]
3393 initWithTitle:@"Name"
3395 width:[self frame].size.width
3398 UITable *table = [list_ table];
3399 [table setSeparatorStyle:1];
3400 [table addTableColumn:column];
3401 [table setDelegate:self];
3402 [table setReusesTableCells:YES];
3404 [self addSubview:list_];
3409 - (void) setDelegate:(id)delegate {
3410 delegate_ = delegate;
3413 - (void) setObject:(id)object {
3419 object_ = [object retain];
3422 - (void) reloadData {
3423 NSArray *packages = [database_ packages];
3425 [packages_ removeAllObjects];
3426 [sections_ removeAllObjects];
3428 for (size_t i(0); i != [packages count]; ++i) {
3429 Package *package([packages objectAtIndex:i]);
3430 if ([[package performSelector:filter_ withObject:object_] boolValue])
3431 [packages_ addObject:package];
3434 Section *section = nil;
3436 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3437 Package *package = [packages_ objectAtIndex:offset];
3438 NSString *name = [package index];
3440 if (section == nil || ![[section name] isEqualToString:name]) {
3441 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3442 [sections_ addObject:section];
3445 [section addToCount];
3451 - (NSString *) title {
3455 - (void) resetViewAnimated:(BOOL)animated {
3456 [list_ resetViewAnimated:animated];
3459 - (void) resetCursor {
3460 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3463 - (UISectionList *) list {
3467 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3468 [list_ setShouldHideHeaderInShortLists:hide];
3474 /* Browser Implementation {{{ */
3475 @implementation BrowserView
3478 WebView *webview = [webview_ webView];
3479 [webview setFrameLoadDelegate:nil];
3480 [webview setResourceLoadDelegate:nil];
3481 [webview setUIDelegate:nil];
3483 [scroller_ setDelegate:nil];
3484 [webview_ setDelegate:nil];
3486 [scroller_ release];
3489 [indicator_ release];
3495 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3496 NSMutableURLRequest *request = [NSMutableURLRequest
3499 timeoutInterval:30.0
3502 [request addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3503 [request addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
3504 [request addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3506 [self loadRequest:request];
3510 - (void) loadURL:(NSURL *)url {
3511 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3514 // XXX: this needs to add the headers
3515 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3519 - (void) loadRequest:(NSURLRequest *)request {
3520 [webview_ loadRequest:request];
3523 - (void) reloadURL {
3524 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3525 [urls_ removeLastObject];
3526 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3529 - (WebView *) webView {
3530 return [webview_ webView];
3533 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3534 [scroller_ setContentSize:frame.size];
3537 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3538 [self view:sender didSetFrame:frame];
3541 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3542 return [self _addHeadersToRequest:request];
3545 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3546 if ([[[request URL] scheme] isEqualToString:@"apptapp"])
3548 [self setBackButtonTitle:title_];
3549 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3550 [browser setDelegate:delegate_];
3551 [book_ pushPage:browser];
3552 [browser loadRequest:[self _addHeadersToRequest:request]];
3553 return [browser webView];
3556 - (void) webView:(WebView *)sender willClickElement:(id)element {
3557 if (![element respondsToSelector:@selector(href)])
3559 NSString *href = [element href];
3562 if ([href hasPrefix:@"apptapp://package/"]) {
3563 NSString *name = [href substringFromIndex:18];
3564 Package *package = [database_ packageWithName:name];
3565 if (package == nil) {
3566 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3567 initWithTitle:@"Cannot Locate Package"
3568 buttons:[NSArray arrayWithObjects:@"Close", nil]
3569 defaultButtonIndex:0
3574 [sheet setBodyText:[NSString stringWithFormat:
3575 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
3578 [sheet popupAlertAnimated:YES];
3580 [self setBackButtonTitle:title_];
3581 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3582 [view setDelegate:delegate_];
3583 [view setPackage:package];
3584 [book_ pushPage:view];
3589 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3590 title_ = [title retain];
3591 [self setTitle:title];
3594 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3595 if ([frame parentFrame] != nil)
3600 [indicator_ startAnimation];
3601 [self reloadButtons];
3603 if (title_ != nil) {
3608 [self setTitle:@"Loading..."];
3610 WebView *webview = [webview_ webView];
3611 NSString *href = [webview mainFrameURL];
3612 [urls_ addObject:[NSURL URLWithString:href]];
3614 CGRect webrect = [scroller_ frame];
3615 webrect.size.height = 0;
3616 [webview_ setFrame:webrect];
3619 - (void) _finishLoading {
3622 [indicator_ stopAnimation];
3623 [self reloadButtons];
3627 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3628 if ([frame parentFrame] != nil)
3630 [self _finishLoading];
3633 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3634 if ([frame parentFrame] != nil)
3636 [self setTitle:[error localizedDescription]];
3637 [self _finishLoading];
3640 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3641 if ((self = [super initWithBook:book]) != nil) {
3642 database_ = database;
3645 struct CGRect bounds = [self bounds];
3647 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3648 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3649 [self addSubview:pinstripe];
3651 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3652 [self addSubview:scroller_];
3654 [scroller_ setScrollingEnabled:YES];
3655 [scroller_ setAdjustForContentSizeChange:YES];
3656 [scroller_ setClipsSubviews:YES];
3657 [scroller_ setAllowsRubberBanding:YES];
3658 [scroller_ setScrollDecelerationFactor:0.99];
3659 [scroller_ setDelegate:self];
3661 CGRect webrect = [scroller_ bounds];
3662 webrect.size.height = 0;
3664 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3665 [scroller_ addSubview:webview_];
3667 [webview_ setTilingEnabled:YES];
3668 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3669 [webview_ setAutoresizes:YES];
3670 [webview_ setDelegate:self];
3671 //[webview_ setEnabledGestures:2];
3673 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
3674 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
3675 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
3677 Package *package([database_ packageWithName:@"cydia"]);
3678 NSString *application = package == nil ? @"Cydia" : [NSString
3679 stringWithFormat:@"Cydia/%@",
3683 WebView *webview = [webview_ webView];
3684 [webview setApplicationNameForUserAgent:application];
3685 [webview setFrameLoadDelegate:self];
3686 [webview setResourceLoadDelegate:self];
3687 [webview setUIDelegate:self];
3689 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3693 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3697 - (void) _leftButtonClicked {
3698 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3699 initWithTitle:@"About Cydia Installer"
3700 buttons:[NSArray arrayWithObjects:@"Close", nil]
3701 defaultButtonIndex:0
3707 @"Copyright (C) 2008\n"
3708 "Jay Freeman (saurik)\n"
3709 "saurik@saurik.com\n"
3710 "http://www.saurik.com/\n"
3713 "http://www.theokorigroup.com/\n"
3715 "College of Creative Studies,\n"
3716 "University of California,\n"
3718 "http://www.ccs.ucsb.edu/"
3721 [sheet popupAlertAnimated:YES];
3724 - (void) _rightButtonClicked {
3729 - (NSString *) leftButtonTitle {
3733 - (NSString *) rightButtonTitle {
3734 return loading_ ? @"" : @"Reload";
3737 - (NSString *) title {
3741 - (NSString *) backButtonTitle {
3745 - (void) setPageActive:(BOOL)active {
3747 [book_ addSubview:indicator_];
3749 [indicator_ removeFromSuperview];
3752 - (void) resetViewAnimated:(BOOL)animated {
3758 @interface CYBook : RVBook <
3761 _transient Database *database_;
3763 UIProgressIndicator *indicator_;
3764 UITextLabel *prompt_;
3765 UIProgressBar *progress_;
3769 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3775 /* Install View {{{ */
3776 @interface InstallView : RVPage {
3777 _transient Database *database_;
3778 NSMutableArray *packages_;
3779 NSMutableArray *sections_;
3784 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3785 - (void) reloadData;
3789 @implementation InstallView
3792 [list_ setDataSource:nil];
3793 [list_ setDelegate:nil];
3795 [packages_ release];
3796 [sections_ release];
3798 [accessory_ release];
3802 - (int) numberOfRowsInTable:(UITable *)table {
3803 return [sections_ count] + 1;
3806 - (float) table:(UITable *)table heightForRow:(int)row {
3810 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3812 reusing = [[[SectionCell alloc] init] autorelease];
3813 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3817 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3821 - (void) tableRowSelected:(NSNotification *)notification {
3822 int row = [[notification object] selectedRow];
3833 title = @"All Packages";
3835 section = [sections_ objectAtIndex:(row - 1)];
3836 name = [section name];
3842 title = @"(No Section)";
3846 PackageTable *table = [[[PackageTable alloc]
3850 filter:@selector(isUninstalledInSection:)
3854 [table setDelegate:delegate_];
3856 [book_ pushPage:table];
3859 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3860 if ((self = [super initWithBook:book]) != nil) {
3861 database_ = database;
3863 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3864 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3866 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3867 [self addSubview:list_];
3869 UITableColumn *column = [[[UITableColumn alloc]
3870 initWithTitle:@"Name"
3872 width:[self frame].size.width
3875 [list_ setDataSource:self];
3876 [list_ setSeparatorStyle:1];
3877 [list_ addTableColumn:column];
3878 [list_ setDelegate:self];
3879 [list_ setReusesTableCells:YES];
3885 - (void) reloadData {
3886 NSArray *packages = [database_ packages];
3888 [packages_ removeAllObjects];
3889 [sections_ removeAllObjects];
3891 for (size_t i(0); i != [packages count]; ++i) {
3892 Package *package([packages objectAtIndex:i]);
3893 if ([package valid] && [package installed] == nil)
3894 [packages_ addObject:package];
3897 [packages_ sortUsingSelector:@selector(compareBySection:)];
3899 Section *section = nil;
3900 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3901 Package *package = [packages_ objectAtIndex:offset];
3902 NSString *name = [package section];
3904 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3905 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3906 [sections_ addObject:section];
3909 [section addToCount];
3915 - (void) resetViewAnimated:(BOOL)animated {
3916 [list_ resetViewAnimated:animated];
3919 - (NSString *) title {
3923 - (NSString *) backButtonTitle {
3927 - (UIView *) accessoryView {
3933 /* Changes View {{{ */
3934 @interface ChangesView : RVPage {
3935 _transient Database *database_;
3936 NSMutableArray *packages_;
3937 NSMutableArray *sections_;
3938 UISectionList *list_;
3942 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3943 - (void) reloadData;
3947 @implementation ChangesView
3950 [[list_ table] setDelegate:nil];
3951 [list_ setDataSource:nil];
3953 [packages_ release];
3954 [sections_ release];
3959 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3960 return [sections_ count];
3963 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3964 return [[sections_ objectAtIndex:section] name];
3967 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3968 return [[sections_ objectAtIndex:section] row];
3971 - (int) numberOfRowsInTable:(UITable *)table {
3972 return [packages_ count];
3975 - (float) table:(UITable *)table heightForRow:(int)row {
3976 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3979 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3981 reusing = [[[PackageCell alloc] init] autorelease];
3982 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3986 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3990 - (void) tableRowSelected:(NSNotification *)notification {
3991 int row = [[notification object] selectedRow];
3994 Package *package = [packages_ objectAtIndex:row];
3995 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3996 [view setDelegate:delegate_];
3997 [view setPackage:package];
3998 [book_ pushPage:view];
4001 - (void) _leftButtonClicked {
4002 [(CYBook *)book_ update];
4003 [self reloadButtons];
4006 - (void) _rightButtonClicked {
4007 [delegate_ distUpgrade];
4010 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4011 if ((self = [super initWithBook:book]) != nil) {
4012 database_ = database;
4014 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4015 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4017 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4018 [self addSubview:list_];
4020 [list_ setShouldHideHeaderInShortLists:NO];
4021 [list_ setDataSource:self];
4022 //[list_ setSectionListStyle:1];
4024 UITableColumn *column = [[[UITableColumn alloc]
4025 initWithTitle:@"Name"
4027 width:[self frame].size.width
4030 UITable *table = [list_ table];
4031 [table setSeparatorStyle:1];
4032 [table addTableColumn:column];
4033 [table setDelegate:self];
4034 [table setReusesTableCells:YES];
4040 - (void) reloadData {
4041 NSArray *packages = [database_ packages];
4043 [packages_ removeAllObjects];
4044 [sections_ removeAllObjects];
4046 for (size_t i(0); i != [packages count]; ++i) {
4047 Package *package([packages objectAtIndex:i]);
4048 if ([package installed] == nil && [package valid] || [package upgradableAndEssential:NO])
4049 [packages_ addObject:package];
4052 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4054 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
4055 Section *section = nil;
4058 bool unseens = false;
4060 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4062 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4063 Package *package = [packages_ objectAtIndex:offset];
4065 if ([package upgradableAndEssential:YES]) {
4067 [upgradable addToCount];
4070 NSDate *seen = [package seen];
4075 name = [@"n/a ?" retain];
4077 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4080 if (section == nil || ![[section name] isEqualToString:name]) {
4081 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4082 [sections_ addObject:section];
4086 [section addToCount];
4090 CFRelease(formatter);
4093 Section *last = [sections_ lastObject];
4094 size_t count = [last count];
4095 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4096 [sections_ removeLastObject];
4100 [sections_ insertObject:upgradable atIndex:0];
4103 [self reloadButtons];
4106 - (void) resetViewAnimated:(BOOL)animated {
4107 [list_ resetViewAnimated:animated];
4110 - (NSString *) leftButtonTitle {
4111 return [(CYBook *)book_ updating] ? nil : @"Refresh";
4114 - (NSString *) rightButtonTitle {
4115 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
4118 - (NSString *) title {
4124 /* Manage View {{{ */
4125 @interface ManageView : PackageTable {
4128 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4132 @implementation ManageView
4134 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4139 filter:@selector(isInstalledInSection:)
4145 - (NSString *) title {
4146 return @"Installed Packages";
4149 - (NSString *) backButtonTitle {
4150 return @"All Packages";
4155 /* Search View {{{ */
4156 @protocol SearchViewDelegate
4157 - (void) showKeyboard:(BOOL)show;
4160 @interface SearchView : RVPage {
4162 UISearchField *field_;
4163 UITransitionView *transition_;
4164 PackageTable *table_;
4165 UIPreferencesTable *advanced_;
4171 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4172 - (void) reloadData;
4176 @implementation SearchView
4180 [[field_ textTraits] setEditingDelegate:nil];
4182 [field_ setDelegate:nil];
4184 [accessory_ release];
4186 [transition_ release];
4188 [advanced_ release];
4193 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4197 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4199 case 0: return @"Advanced Search (Coming Soon!)";
4201 default: _assert(false);
4205 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4209 default: _assert(false);
4213 - (void) _showKeyboard:(BOOL)show {
4214 CGSize keysize = [UIKeyboard defaultSize];
4215 CGRect keydown = [book_ pageBounds];
4216 CGRect keyup = keydown;
4217 keyup.size.height -= keysize.height - ButtonBarHeight_;
4219 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4221 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4222 [animation setSignificantRectFields:8];
4225 [animation setStartFrame:keydown];
4226 [animation setEndFrame:keyup];
4228 [animation setStartFrame:keyup];
4229 [animation setEndFrame:keydown];
4232 UIAnimator *animator = [UIAnimator sharedAnimator];
4235 addAnimations:[NSArray arrayWithObjects:animation, nil]
4236 withDuration:(KeyboardTime_ - delay)
4241 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4244 [delegate_ showKeyboard:show];
4248 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
4249 [self _showKeyboard:YES];
4252 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
4253 [self _showKeyboard:NO];
4256 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
4258 NSString *text([field_ text]);
4259 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
4265 - (void) textFieldClearButtonPressed:(UITextField *)field {
4269 - (void) keyboardInputShouldDelete:(id)input {
4273 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
4274 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
4278 [field_ resignFirstResponder];
4283 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4284 if ((self = [super initWithBook:book]) != nil) {
4285 CGRect pageBounds = [book_ pageBounds];
4287 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
4288 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4289 [self addSubview:pinstripe];*/
4291 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
4292 [self addSubview:transition_];
4294 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
4296 [advanced_ setReusesTableCells:YES];
4297 [advanced_ setDataSource:self];
4298 [advanced_ reloadData];
4300 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
4301 CGColor dimmed(space_, 0, 0, 0, 0.5);
4302 [dimmed_ setBackgroundColor:dimmed];
4304 table_ = [[PackageTable alloc]
4308 filter:@selector(isSearchedForBy:)
4312 [table_ setShouldHideHeaderInShortLists:NO];
4313 [transition_ transition:0 toView:table_];
4322 area.origin.x = cnfrect.origin.x + cnfrect.size.width + 14;
4329 [self bounds].size.width - area.origin.x - 18;
4331 area.size.height = [UISearchField defaultHeight];
4333 field_ = [[UISearchField alloc] initWithFrame:area];
4335 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
4336 [field_ setFont:font];
4339 [field_ setPlaceholder:@"Package Names & Descriptions"];
4340 [field_ setDelegate:self];
4343 [field_ setPaddingTop:3];
4345 [field_ setPaddingTop:5];
4349 UITextTraits *traits = [field_ textTraits];
4350 [traits setEditingDelegate:self];
4351 [traits setReturnKeyType:6];
4352 [traits setAutoCapsType:0];
4353 [traits setAutoCorrectionType:1];
4356 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
4358 accessory_ = [[UIView alloc] initWithFrame:accrect];
4359 [accessory_ addSubview:field_];
4361 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
4362 [configure setShowPressFeedback:YES];
4363 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
4364 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
4365 [accessory_ addSubview:configure];
4371 LKAnimation *animation = [LKTransition animation];
4372 [animation setType:@"oglFlip"];
4373 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
4374 [animation setFillMode:@"extended"];
4375 [animation setTransitionFlags:3];
4376 [animation setDuration:10];
4377 [animation setSpeed:0.35];
4378 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
4379 [[transition_ _layer] addAnimation:animation forKey:0];
4380 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
4381 flipped_ = !flipped_;
4385 - (void) configurePushed {
4386 [field_ resignFirstResponder];
4390 - (void) resetViewAnimated:(BOOL)animated {
4393 [table_ resetViewAnimated:animated];
4396 - (void) reloadData {
4399 [table_ setObject:[field_ text]];
4400 [table_ reloadData];
4401 [table_ resetCursor];
4404 - (UIView *) accessoryView {
4408 - (NSString *) title {
4412 - (NSString *) backButtonTitle {
4416 - (void) setDelegate:(id)delegate {
4417 [table_ setDelegate:delegate];
4418 [super setDelegate:delegate];
4424 @implementation CYBook
4428 [indicator_ release];
4430 [progress_ release];
4434 - (NSString *) getTitleForPage:(RVPage *)page {
4435 return Simplify([super getTitleForPage:page]);
4443 [navbar_ setPrompt:@""];
4444 [navbar_ addSubview:overlay_];
4445 [indicator_ startAnimation];
4446 [prompt_ setText:@"Updating Database..."];
4447 [progress_ setProgress:0];
4452 detachNewThreadSelector:@selector(_update)
4461 [overlay_ removeFromSuperview];
4462 [indicator_ stopAnimation];
4463 [delegate_ reloadData];
4465 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
4468 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4469 if ((self = [super initWithFrame:frame]) != nil) {
4470 database_ = database;
4473 [navbar_ setBarStyle:1];
4475 CGRect ovrrect = [navbar_ bounds];
4476 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
4478 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
4480 UIProgressIndicatorStyle style = Advanced_ ?
4481 kUIProgressIndicatorStyleSmallWhite :
4482 kUIProgressIndicatorStyleSmallBlack;
4484 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4485 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4486 CGRect indrect = {{indoffset, indoffset}, indsize};
4488 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4489 [indicator_ setStyle:style];
4490 [overlay_ addSubview:indicator_];
4492 CGSize prmsize = {200, indsize.width + 4};
4495 indoffset * 2 + indsize.width,
4499 (ovrrect.size.height - prmsize.height) / 2
4502 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
4504 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4506 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
4507 [prompt_ setBackgroundColor:Clear_];
4508 [prompt_ setFont:font];
4512 [overlay_ addSubview:prompt_];
4514 CGSize prgsize = {75, 100};
4517 ovrrect.size.width - prgsize.width - 10,
4518 (ovrrect.size.height - prgsize.height) / 2
4521 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4522 [progress_ setStyle:0];
4523 [overlay_ addSubview:progress_];
4528 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4531 status.setDelegate(self);
4533 [database_ updateWithStatus:status];
4536 performSelectorOnMainThread:@selector(_update_)
4544 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
4545 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4548 - (void) setProgressTitle:(NSString *)title {
4550 performSelectorOnMainThread:@selector(_setProgressTitle:)
4556 - (void) setProgressPercent:(float)percent {
4559 - (void) addProgressOutput:(NSString *)output {
4561 performSelectorOnMainThread:@selector(_addProgressOutput:)
4567 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4571 - (void) _setProgressTitle:(NSString *)title {
4572 [prompt_ setText:[title stringByAppendingString:@"..."]];
4575 - (void) _addProgressOutput:(NSString *)output {
4580 @interface Cydia : UIApplication <
4581 ConfirmationViewDelegate,
4582 ProgressViewDelegate,
4591 UIButtonBar *buttonbar_;
4593 ConfirmationView *confirm_;
4595 NSMutableArray *essential_;
4596 NSMutableArray *broken_;
4598 Database *database_;
4599 ProgressView *progress_;
4603 UIKeyboard *keyboard_;
4605 InstallView *install_;
4606 ChangesView *changes_;
4607 ManageView *manage_;
4608 SearchView *search_;
4613 @implementation Cydia
4616 if ([broken_ count] != 0) {
4617 int count = [broken_ count];
4619 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4620 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
4621 buttons:[NSArray arrayWithObjects:
4623 @"Ignore (Temporary)",
4625 defaultButtonIndex:0
4630 [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."];
4631 [sheet popupAlertAnimated:YES];
4632 } else if (!Ignored_ && [essential_ count] != 0) {
4633 int count = [essential_ count];
4635 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4636 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
4637 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
4638 defaultButtonIndex:0
4643 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
4644 [sheet popupAlertAnimated:YES];
4648 - (void) _reloadData {
4649 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4650 [hud setText:@"Reloading Data"];
4651 [overlay_ addSubview:hud];
4654 [database_ reloadData];
4656 if (Packages_ == nil) {
4657 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:128];
4658 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4663 [essential_ removeAllObjects];
4664 [broken_ removeAllObjects];
4666 NSArray *packages = [database_ packages];
4667 for (int i(0), e([packages count]); i != e; ++i) {
4668 Package *package = [packages objectAtIndex:i];
4670 [broken_ addObject:package];
4671 if ([package upgradableAndEssential:NO]) {
4672 if ([package essential])
4673 [essential_ addObject:package];
4679 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4680 [buttonbar_ setBadgeValue:badge forButton:3];
4681 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4682 [buttonbar_ setBadgeAnimated:YES forButton:3];
4683 [self setApplicationBadge:badge];
4685 [buttonbar_ setBadgeValue:nil forButton:3];
4686 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4687 [buttonbar_ setBadgeAnimated:NO forButton:3];
4688 [self removeApplicationBadge];
4692 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4696 /* XXX: this is just stupid */
4698 [install_ reloadData];
4700 [changes_ reloadData];
4702 [manage_ reloadData];
4704 [search_ reloadData];
4708 if ([packages count] == 0);
4717 [hud removeFromSuperview];*/
4720 - (void) reloadData {
4721 @synchronized (self) {
4722 if (confirm_ == nil)
4728 pkgProblemResolver *resolver = [database_ resolver];
4730 resolver->InstallProtect();
4731 if (!resolver->Resolve(true))
4736 [database_ prepare];
4738 if ([database_ cache]->BrokenCount() == 0)
4739 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4741 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4742 NSArray *packages = [database_ packages];
4744 for (size_t i(0); i != [packages count]; ++i) {
4745 Package *package = [packages objectAtIndex:i];
4746 if ([package broken])
4747 [broken addObject:[package name]];
4750 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4751 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4752 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4753 defaultButtonIndex:0
4758 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4759 [sheet popupAlertAnimated:YES];
4765 - (void) installPackage:(Package *)package {
4766 @synchronized (self) {
4773 - (void) removePackage:(Package *)package {
4774 @synchronized (self) {
4781 - (void) distUpgrade {
4782 @synchronized (self) {
4783 [database_ upgrade];
4789 @synchronized (self) {
4797 [overlay_ removeFromSuperview];
4801 detachNewThreadSelector:@selector(perform)
4808 - (void) bootstrap_ {
4810 [database_ upgrade];
4811 [database_ prepare];
4812 [database_ perform];
4815 - (void) bootstrap {
4817 detachNewThreadSelector:@selector(bootstrap_)
4820 title:@"Bootstrap Install..."
4824 - (void) progressViewIsComplete:(ProgressView *)progress {
4825 @synchronized (self) {
4828 if (confirm_ != nil) {
4829 [underlay_ addSubview:overlay_];
4830 [confirm_ removeFromSuperview];
4837 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4838 NSString *context = [sheet context];
4839 if ([context isEqualToString:@"fixhalf"])
4842 @synchronized (self) {
4843 for (int i = 0, e = [broken_ count]; i != e; ++i) {
4844 Package *broken = [broken_ objectAtIndex:i];
4847 NSString *id = [broken id];
4848 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
4849 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
4850 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
4851 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
4860 [broken_ removeAllObjects];
4867 else if ([context isEqualToString:@"upgrade"])
4870 @synchronized (self) {
4871 for (int i = 0, e = [essential_ count]; i != e; ++i) {
4872 Package *essential = [essential_ objectAtIndex:i];
4873 [essential install];
4892 - (void) setPage:(RVPage *)page {
4893 [page resetViewAnimated:NO];
4894 [page setDelegate:self];
4895 [book_ setPage:page];
4898 - (RVPage *) _setHomePage {
4899 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4900 [self setPage:browser];
4901 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4905 - (void) buttonBarItemTapped:(id)sender {
4906 unsigned tag = [sender tag];
4908 [book_ resetViewAnimated:YES];
4913 case 1: [self _setHomePage]; break;
4915 case 2: [self setPage:install_]; break;
4916 case 3: [self setPage:changes_]; break;
4917 case 4: [self setPage:manage_]; break;
4918 case 5: [self setPage:search_]; break;
4920 default: _assert(false);
4926 - (void) applicationWillSuspend {
4927 [super applicationWillSuspend];
4932 pid_t pid = ExecFork();
4935 if (pid_t child = fork())
4936 waitpid(child, NULL, 0);
4938 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
4941 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
4947 - (void) applicationDidFinishLaunching:(id)unused {
4948 _assert(pkgInitConfig(*_config));
4949 _assert(pkgInitSystem(*_config, _system));
4954 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
4955 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
4957 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4958 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4960 [window_ orderFront: self];
4961 [window_ makeKey: self];
4962 [window_ _setHidden: NO];
4964 database_ = [[Database alloc] init];
4965 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
4966 [database_ setDelegate:progress_];
4967 [window_ setContentView:progress_];
4969 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4970 [progress_ setContentView:underlay_];
4972 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4975 [underlay_ addSubview:overlay_];
4977 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4978 0, 0, screenrect.size.width, screenrect.size.height - 48
4979 ) database:database_];
4981 [book_ setDelegate:self];
4983 [overlay_ addSubview:book_];
4985 NSArray *buttonitems = [NSArray arrayWithObjects:
4986 [NSDictionary dictionaryWithObjectsAndKeys:
4987 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4988 @"home-up.png", kUIButtonBarButtonInfo,
4989 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
4990 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4991 self, kUIButtonBarButtonTarget,
4992 @"Home", kUIButtonBarButtonTitle,
4993 @"0", kUIButtonBarButtonType,
4996 [NSDictionary dictionaryWithObjectsAndKeys:
4997 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4998 @"install-up.png", kUIButtonBarButtonInfo,
4999 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5000 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5001 self, kUIButtonBarButtonTarget,
5002 @"Install", kUIButtonBarButtonTitle,
5003 @"0", kUIButtonBarButtonType,
5006 [NSDictionary dictionaryWithObjectsAndKeys:
5007 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5008 @"changes-up.png", kUIButtonBarButtonInfo,
5009 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5010 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5011 self, kUIButtonBarButtonTarget,
5012 @"Changes", kUIButtonBarButtonTitle,
5013 @"0", kUIButtonBarButtonType,
5016 [NSDictionary dictionaryWithObjectsAndKeys:
5017 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5018 @"manage-up.png", kUIButtonBarButtonInfo,
5019 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5020 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5021 self, kUIButtonBarButtonTarget,
5022 @"Manage", kUIButtonBarButtonTitle,
5023 @"0", kUIButtonBarButtonType,
5026 [NSDictionary dictionaryWithObjectsAndKeys:
5027 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5028 @"search-up.png", kUIButtonBarButtonInfo,
5029 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5030 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5031 self, kUIButtonBarButtonTarget,
5032 @"Search", kUIButtonBarButtonTitle,
5033 @"0", kUIButtonBarButtonType,
5037 buttonbar_ = [[UIButtonBar alloc]
5039 withFrame:CGRectMake(
5040 0, screenrect.size.height - ButtonBarHeight_,
5041 screenrect.size.width, ButtonBarHeight_
5043 withItemList:buttonitems
5046 [buttonbar_ setDelegate:self];
5047 [buttonbar_ setBarStyle:1];
5048 [buttonbar_ setButtonBarTrackingMode:2];
5050 int buttons[5] = {1, 2, 3, 4, 5};
5051 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5052 [buttonbar_ showButtonGroup:0 withDuration:0];
5054 for (int i = 0; i != 5; ++i)
5055 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5056 i * 64 + 2, 1, 60, ButtonBarHeight_
5059 [buttonbar_ showSelectionForButton:1];
5060 [overlay_ addSubview:buttonbar_];
5062 [UIKeyboard initImplementationNow];
5063 CGSize keysize = [UIKeyboard defaultSize];
5064 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5065 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5066 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5067 [overlay_ addSubview:keyboard_];
5069 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5070 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5071 manage_ = [[ManageView alloc] initWithBook:book_ database:database_];
5072 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5074 [progress_ resetView];
5080 [self _setHomePage];
5083 - (void) showKeyboard:(BOOL)show {
5084 CGSize keysize = [UIKeyboard defaultSize];
5085 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
5086 CGRect keyup = keydown;
5087 keyup.origin.y -= keysize.height;
5089 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
5090 [animation setSignificantRectFields:2];
5093 [animation setStartFrame:keydown];
5094 [animation setEndFrame:keyup];
5095 [keyboard_ activate];
5097 [animation setStartFrame:keyup];
5098 [animation setEndFrame:keydown];
5099 [keyboard_ deactivate];
5102 [[UIAnimator sharedAnimator]
5103 addAnimations:[NSArray arrayWithObjects:animation, nil]
5104 withDuration:KeyboardTime_
5109 - (void) slideUp:(UIAlertSheet *)alert {
5111 [alert presentSheetFromButtonBar:buttonbar_];
5113 [alert presentSheetInView:overlay_];
5118 void AddPreferences(NSString *plist) {
5119 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5121 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
5122 _assert(settings != NULL);
5123 NSMutableArray *items = [settings objectForKey:@"items"];
5127 for (size_t i(0); i != [items count]; ++i) {
5128 NSMutableDictionary *item([items objectAtIndex:i]);
5129 NSString *label = [item objectForKey:@"label"];
5130 if (label != nil && [label isEqualToString:@"Cydia"]) {
5137 for (size_t i(0); i != [items count]; ++i) {
5138 NSDictionary *item([items objectAtIndex:i]);
5139 NSString *label = [item objectForKey:@"label"];
5140 if (label != nil && [label isEqualToString:@"General"]) {
5141 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
5142 @"CydiaSettings", @"bundle",
5143 @"PSLinkCell", @"cell",
5144 [NSNumber numberWithBool:YES], @"hasIcon",
5145 [NSNumber numberWithBool:YES], @"isController",
5147 nil] atIndex:(i + 1)];
5153 _assert([settings writeToFile:plist atomically:YES] == YES);
5160 id Alloc_(id self, SEL selector) {
5161 id object = alloc_(self, selector);
5162 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
5167 id Dealloc_(id self, SEL selector) {
5168 id object = dealloc_(self, selector);
5169 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
5173 int main(int argc, char *argv[]) {
5174 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5176 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
5178 Home_ = NSHomeDirectory();
5181 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
5182 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
5183 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
5184 Sounds_Keyboard_ = [keyboard boolValue];
5190 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
5191 alloc_ = alloc->method_imp;
5192 alloc->method_imp = (IMP) &Alloc_;*/
5194 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
5195 dealloc_ = dealloc->method_imp;
5196 dealloc->method_imp = (IMP) &Dealloc_;*/
5198 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
5199 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5200 Firmware_ = strdup([prover UTF8String]);
5201 NSArray *versions = [prover componentsSeparatedByString:@"."];
5202 int count = [versions count];
5203 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
5204 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
5205 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
5210 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
5211 char *machine = new char[size];
5212 sysctlbyname("hw.machine", machine, &size, NULL, 0);
5215 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
5216 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
5217 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
5218 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
5222 IOObjectRelease(service);
5225 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
5226 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
5228 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
5229 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
5231 Packages_ = [Metadata_ objectForKey:@"Packages"];
5233 if (access("/User", F_OK) != 0)
5234 system("/usr/libexec/cydia/firmware.sh");
5236 Locale_ = CFLocaleCopyCurrent();
5237 space_ = CGColorSpaceCreateDeviceRGB();
5239 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
5240 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
5241 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
5242 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
5243 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
5245 int value = UIApplicationMain(argc, argv, [Cydia class]);
5247 CGColorSpaceRelease(space_);