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_;
2163 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2165 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2166 - (void) setContentView:(UIView *)view;
2169 - (void) _retachThread;
2170 - (void) _detachNewThreadData:(ProgressData *)data;
2171 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2177 @protocol ProgressViewDelegate
2178 - (void) progressViewIsComplete:(ProgressView *)sender;
2181 @implementation ProgressView
2184 [transition_ setDelegate:nil];
2185 [navbar_ setDelegate:nil];
2188 if (background_ != nil)
2189 [background_ release];
2190 [transition_ release];
2193 [progress_ release];
2200 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2201 if (bootstrap_ && from == overlay_ && to == view_)
2205 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2206 if ((self = [super initWithFrame:frame]) != nil) {
2207 database_ = database;
2208 delegate_ = delegate;
2210 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2211 [transition_ setDelegate:self];
2213 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2216 [overlay_ setBackgroundColor:Black_];
2218 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2219 [background_ setBackgroundColor:Black_];
2220 [self addSubview:background_];
2223 [self addSubview:transition_];
2225 CGSize navsize = [UINavigationBar defaultSize];
2226 CGRect navrect = {{0, 0}, navsize};
2228 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2229 [overlay_ addSubview:navbar_];
2231 [navbar_ setBarStyle:1];
2232 [navbar_ setDelegate:self];
2234 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2235 [navbar_ pushNavigationItem:navitem];
2237 CGRect bounds = [overlay_ bounds];
2238 CGSize prgsize = [UIProgressBar defaultSize];
2241 (bounds.size.width - prgsize.width) / 2,
2242 bounds.size.height - prgsize.height - 20
2245 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2246 [progress_ setStyle:0];
2248 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2250 bounds.size.height - prgsize.height - 50,
2251 bounds.size.width - 20,
2255 [status_ setColor:White_];
2256 [status_ setBackgroundColor:Clear_];
2258 [status_ setCentersHorizontally:YES];
2259 //[status_ setFont:font];
2261 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2263 navrect.size.height + 20,
2264 bounds.size.width - 20,
2265 bounds.size.height - navsize.height - 62 - navrect.size.height
2268 //[output_ setTextFont:@"Courier New"];
2269 [output_ setTextSize:12];
2271 [output_ setTextColor:White_];
2272 [output_ setBackgroundColor:Clear_];
2274 [output_ setMarginTop:0];
2275 [output_ setAllowsRubberBanding:YES];
2276 [output_ setEditable:NO];
2278 [overlay_ addSubview:output_];
2280 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2282 bounds.size.height - prgsize.height - 50,
2283 bounds.size.width - 20,
2287 [close_ setAutosizesToFit:NO];
2288 [close_ setDrawsShadow:YES];
2289 [close_ setStretchBackground:YES];
2290 [close_ setTitle:@"Close Window"];
2291 [close_ setEnabled:YES];
2293 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2294 [close_ setTitleFont:bold];
2297 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2298 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2299 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2303 - (void) setContentView:(UIView *)view {
2304 view_ = [view retain];
2307 - (void) resetView {
2308 [transition_ transition:6 toView:view_];
2311 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2312 NSString *context = [sheet context];
2313 if ([context isEqualToString:@"conffile"]) {
2314 FILE *input = [database_ input];
2318 fprintf(input, "N\n");
2322 fprintf(input, "Y\n");
2333 - (void) closeButtonPushed {
2334 [delegate_ progressViewIsComplete:self];
2338 - (void) _retachThread {
2339 UINavigationItem *item = [navbar_ topItem];
2340 [item setTitle:@"Complete"];
2342 [overlay_ addSubview:close_];
2343 [progress_ removeFromSuperview];
2344 [status_ removeFromSuperview];
2346 [delegate_ setStatusBarShowsProgress:NO];
2350 - (void) _detachNewThreadData:(ProgressData *)data {
2351 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2353 [[data target] performSelector:[data selector] withObject:[data object]];
2356 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2361 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2362 UINavigationItem *item = [navbar_ topItem];
2363 [item setTitle:title];
2365 [status_ setText:nil];
2366 [output_ setText:@""];
2367 [progress_ setProgress:0];
2369 [close_ removeFromSuperview];
2370 [overlay_ addSubview:progress_];
2371 [overlay_ addSubview:status_];
2373 [delegate_ setStatusBarShowsProgress:YES];
2376 [transition_ transition:6 toView:overlay_];
2379 detachNewThreadSelector:@selector(_detachNewThreadData:)
2381 withObject:[[ProgressData alloc]
2382 initWithSelector:selector
2389 - (void) repairWithSelector:(SEL)selector {
2391 detachNewThreadSelector:selector
2394 title:@"Repairing..."
2398 - (void) setConfigurationData:(NSString *)data {
2400 performSelectorOnMainThread:@selector(_setConfigurationData:)
2406 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2407 Package *package = id == nil ? nil : [database_ packageWithName:id];
2409 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2410 initWithTitle:(package == nil ? @"Source Error" : [package name])
2411 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2412 defaultButtonIndex:0
2417 [sheet setBodyText:error];
2418 [sheet popupAlertAnimated:YES];
2421 - (void) setProgressTitle:(NSString *)title {
2423 performSelectorOnMainThread:@selector(_setProgressTitle:)
2429 - (void) setProgressPercent:(float)percent {
2431 performSelectorOnMainThread:@selector(_setProgressPercent:)
2432 withObject:[NSNumber numberWithFloat:percent]
2437 - (void) addProgressOutput:(NSString *)output {
2439 performSelectorOnMainThread:@selector(_addProgressOutput:)
2445 - (void) _setConfigurationData:(NSString *)data {
2446 _assert(conffile_r(data));
2448 NSString *ofile = conffile_r[1];
2449 //NSString *nfile = conffile_r[2];
2451 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2452 initWithTitle:@"Configuration Upgrade"
2453 buttons:[NSArray arrayWithObjects:
2454 @"Keep My Old Copy",
2455 @"Accept The New Copy",
2456 // XXX: @"See What Changed",
2458 defaultButtonIndex:0
2463 [sheet setBodyText:[NSString stringWithFormat:
2464 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2467 [sheet popupAlertAnimated:YES];
2470 - (void) _setProgressTitle:(NSString *)title {
2471 [status_ setText:[title stringByAppendingString:@"..."]];
2474 - (void) _setProgressPercent:(NSNumber *)percent {
2475 [progress_ setProgress:[percent floatValue]];
2478 - (void) _addProgressOutput:(NSString *)output {
2479 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2480 CGSize size = [output_ contentSize];
2481 CGRect rect = {{0, size.height}, {size.width, 0}};
2482 [output_ scrollRectToVisible:rect animated:YES];
2485 - (BOOL) isRunning {
2492 /* Package Cell {{{ */
2493 @interface PackageCell : UITableCell {
2496 UITextLabel *description_;
2497 UITextLabel *source_;
2498 //UIImageView *trusted_;
2500 UIImageView *badge_;
2501 UITextLabel *status_;
2506 - (PackageCell *) init;
2507 - (void) setPackage:(Package *)package;
2509 - (void) _setSelected:(float)fraction;
2510 - (void) setSelected:(BOOL)selected;
2511 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2512 - (void) _setSelectionFadeFraction:(float)fraction;
2514 + (int) heightForPackage:(Package *)package;
2518 @implementation PackageCell
2523 [description_ release];
2529 //[trusted_ release];
2533 - (PackageCell *) init {
2534 if ((self = [super init]) != nil) {
2535 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2536 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2537 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2539 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2541 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2542 [name_ setBackgroundColor:Clear_];
2543 [name_ setFont:bold];
2545 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2546 [source_ setBackgroundColor:Clear_];
2547 [source_ setFont:large];
2549 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2550 [description_ setBackgroundColor:Clear_];
2551 [description_ setFont:small];
2553 /*trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2554 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];*/
2557 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2559 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2560 [status_ setBackgroundColor:Clear_];
2561 [status_ setFont:small];
2564 /*[icon_ setImage:[UIImage applicationImageNamed:@"unknown.png"]];
2565 [icon_ zoomToScale:0.5];
2566 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];*/
2568 [self addSubview:icon_];
2569 [self addSubview:name_];
2570 [self addSubview:description_];
2571 [self addSubview:source_];
2579 - (void) setPackage:(Package *)package {
2585 Source *source = [package source];
2587 UIImage *image = nil;
2588 if (NSString *icon = [package icon])
2589 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2590 if (image == nil) if (NSString *icon = [source defaultIcon])
2591 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2593 image = [UIImage applicationImageNamed:@"unknown.png"];
2594 [icon_ setImage:image];
2596 /*if (image != nil) {
2597 CGSize size = [image size];
2598 float scale = 30 / std::max(size.width, size.height);
2599 [icon_ zoomToScale:scale];
2602 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2604 [name_ setText:[package name]];
2605 [description_ setText:[package tagline]];
2607 NSString *label = nil;
2608 bool trusted = false;
2610 if (source != nil) {
2611 label = [source label];
2612 trusted = [source trusted];
2613 } else if ([[package id] isEqualToString:@"firmware"])
2617 label = @"Unknown/Local";
2619 NSString *from = [NSString stringWithFormat:@"from %@", label];
2621 NSString *section = Simplify([package section]);
2622 if (section != nil && ![section isEqualToString:label])
2623 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2625 [source_ setText:from];
2628 [badge_ removeFromSuperview];
2629 [status_ removeFromSuperview];
2631 if (NSString *mode = [package mode]) {
2632 [badge_ setImage:[UIImage applicationImageNamed:
2633 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2636 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2637 [status_ setColor:Blueish_];
2638 } else if ([package half]) {
2639 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2640 [status_ setText:@"Package Damaged"];
2641 [status_ setColor:Red_];
2643 [badge_ setImage:nil];
2644 [status_ setText:nil];
2648 [self addSubview:badge_];
2649 [self addSubview:status_];
2654 - (void) _setSelected:(float)fraction {
2655 CGColor black(space_,
2656 Interpolate(0.0, 1.0, fraction),
2657 Interpolate(0.0, 1.0, fraction),
2658 Interpolate(0.0, 1.0, fraction),
2661 CGColor gray(space_,
2662 Interpolate(0.4, 1.0, fraction),
2663 Interpolate(0.4, 1.0, fraction),
2664 Interpolate(0.4, 1.0, fraction),
2667 [name_ setColor:black];
2668 [description_ setColor:gray];
2669 [source_ setColor:black];
2672 - (void) setSelected:(BOOL)selected {
2673 [self _setSelected:(selected ? 1.0 : 0.0)];
2674 [super setSelected:selected];
2677 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2679 [self _setSelected:(selected ? 1.0 : 0.0)];
2680 [super setSelected:selected withFade:fade];
2683 - (void) _setSelectionFadeFraction:(float)fraction {
2684 [self _setSelected:fraction];
2685 [super _setSelectionFadeFraction:fraction];
2688 + (int) heightForPackage:(Package *)package {
2690 if ([package hasMode] || [package half])
2699 /* Section Cell {{{ */
2700 @interface SectionCell : UITableCell {
2702 UITextLabel *count_;
2706 - (void) setSection:(Section *)section;
2708 - (void) _setSelected:(float)fraction;
2709 - (void) setSelected:(BOOL)selected;
2710 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2711 - (void) _setSelectionFadeFraction:(float)fraction;
2715 @implementation SectionCell
2724 if ((self = [super init]) != nil) {
2725 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2726 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2728 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2729 [name_ setBackgroundColor:Clear_];
2730 [name_ setFont:bold];
2732 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2733 [count_ setCentersHorizontally:YES];
2734 [count_ setBackgroundColor:Clear_];
2735 [count_ setFont:small];
2736 [count_ setColor:White_];
2738 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2739 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2741 [self addSubview:folder];
2742 [self addSubview:name_];
2743 [self addSubview:count_];
2745 [self _setSelected:0];
2752 - (void) setSection:(Section *)section {
2753 if (section == nil) {
2754 [name_ setText:@"All Packages"];
2755 [count_ setText:nil];
2757 NSString *name = [section name];
2758 [name_ setText:(name == nil ? @"(No Section)" : name)];
2759 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2763 - (void) _setSelected:(float)fraction {
2764 CGColor black(space_,
2765 Interpolate(0.0, 1.0, fraction),
2766 Interpolate(0.0, 1.0, fraction),
2767 Interpolate(0.0, 1.0, fraction),
2770 [name_ setColor:black];
2773 - (void) setSelected:(BOOL)selected {
2774 [self _setSelected:(selected ? 1.0 : 0.0)];
2775 [super setSelected:selected];
2778 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2780 [self _setSelected:(selected ? 1.0 : 0.0)];
2781 [super setSelected:selected withFade:fade];
2784 - (void) _setSelectionFadeFraction:(float)fraction {
2785 [self _setSelected:fraction];
2786 [super _setSelectionFadeFraction:fraction];
2792 /* File Table {{{ */
2793 @interface FileTable : RVPage {
2794 _transient Database *database_;
2797 NSMutableArray *files_;
2801 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2802 - (void) setPackage:(Package *)package;
2806 @implementation FileTable
2809 if (package_ != nil)
2818 - (int) numberOfRowsInTable:(UITable *)table {
2819 return files_ == nil ? 0 : [files_ count];
2822 - (float) table:(UITable *)table heightForRow:(int)row {
2826 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2827 if (reusing == nil) {
2828 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
2829 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2830 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
2833 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
2837 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
2841 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2842 if ((self = [super initWithBook:book]) != nil) {
2843 database_ = database;
2845 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
2847 list_ = [[UITable alloc] initWithFrame:[self bounds]];
2848 [self addSubview:list_];
2850 UITableColumn *column = [[[UITableColumn alloc]
2851 initWithTitle:@"Name"
2853 width:[self frame].size.width
2856 [list_ setDataSource:self];
2857 [list_ setSeparatorStyle:1];
2858 [list_ addTableColumn:column];
2859 [list_ setDelegate:self];
2860 [list_ setReusesTableCells:YES];
2864 - (void) setPackage:(Package *)package {
2865 if (package_ != nil) {
2866 [package_ autorelease];
2875 [files_ removeAllObjects];
2877 if (package != nil) {
2878 package_ = [package retain];
2879 name_ = [[package id] retain];
2881 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
2884 std::ifstream fin([path UTF8String]);
2886 while (std::getline(fin, line))
2887 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
2890 if ([files_ count] != 0) {
2891 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
2892 [files_ removeObjectAtIndex:0];
2893 [files_ sortUsingSelector:@selector(compareByPath:)];
2895 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
2896 [stack addObject:@"/"];
2898 for (int i(0), e([files_ count]); i != e; ++i) {
2899 NSString *file = [files_ objectAtIndex:i];
2900 while (![file hasPrefix:[stack lastObject]])
2901 [stack removeLastObject];
2902 NSString *directory = [stack lastObject];
2903 [stack addObject:[file stringByAppendingString:@"/"]];
2904 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
2905 ([stack count] - 2) * 3, "",
2906 [file substringFromIndex:[directory length]]
2915 - (void) resetViewAnimated:(BOOL)animated {
2916 [list_ resetViewAnimated:animated];
2919 - (void) reloadData {
2920 [self setPackage:[database_ packageWithName:name_]];
2921 [self reloadButtons];
2924 - (NSString *) title {
2925 return @"Installed Files";
2928 - (NSString *) backButtonTitle {
2934 /* Package View {{{ */
2935 @protocol PackageViewDelegate
2936 - (void) performPackage:(Package *)package;
2939 @interface PackageView : RVPage {
2940 _transient Database *database_;
2941 UIPreferencesTable *table_;
2944 UITextView *description_;
2945 NSMutableArray *buttons_;
2948 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2949 - (void) setPackage:(Package *)package;
2953 @implementation PackageView
2956 [table_ setDataSource:nil];
2957 [table_ setDelegate:nil];
2959 if (package_ != nil)
2963 if (description_ != nil)
2964 [description_ release];
2970 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2972 if ([package_ installed] != nil)
2974 if ([package_ source] != nil)
2979 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2982 else if ([package_ installed] != nil && group-- == 0)
2983 return @"Installed Package";
2984 else if (group-- == 0)
2985 return @"Package Details";
2986 else if ([package_ source] != nil && group-- == 0)
2987 return @"Source Information";
2988 else _assert(false);
2991 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2992 if (description_ == nil || group != 0 || row != 1)
2995 return [description_ visibleTextRect].size.height + TextViewOffset_;
2998 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
3001 if ([package_ author] != nil)
3003 if (description_ != nil)
3005 if ([package_ website] != nil)
3008 } else if ([package_ installed] != nil && group-- == 0)
3010 else if (group-- == 0) {
3012 if ([package_ size] != 0)
3014 if ([package_ maintainer] != nil)
3016 if ([package_ sponsor] != nil)
3018 if ([package_ relationships] != nil)
3020 if ([[package_ source] trusted])
3023 } else if ([package_ source] != nil && group-- == 0) {
3024 Source *source = [package_ source];
3025 NSString *description = [source description];
3027 if (description != nil && ![description isEqualToString:[source label]])
3029 if ([source origin] != nil)
3032 } else _assert(false);
3035 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3036 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3037 [cell setShowSelection:NO];
3041 } else if (row-- == 0) {
3042 [cell setTitle:[package_ name]];
3043 [cell setValue:[package_ latest]];
3044 } else if ([package_ author] != nil && row-- == 0) {
3045 [cell setTitle:@"Author"];
3046 [cell setValue:[[package_ author] name]];
3047 [cell setShowDisclosure:YES];
3048 [cell setShowSelection:YES];
3049 } else if (description_ != nil && row-- == 0) {
3050 [cell addSubview:description_];
3051 } else if ([package_ website] != nil && row-- == 0) {
3052 [cell setTitle:@"More Information"];
3053 [cell setShowDisclosure:YES];
3054 [cell setShowSelection:YES];
3055 } else _assert(false);
3056 } else if ([package_ installed] != nil && group-- == 0) {
3058 } else if (row-- == 0) {
3059 [cell setTitle:@"Version"];
3060 NSString *installed([package_ installed]);
3061 [cell setValue:(installed == nil ? @"n/a" : installed)];
3062 } else if (row-- == 0) {
3063 [cell setTitle:@"Filesystem Content"];
3064 [cell setShowDisclosure:YES];
3065 [cell setShowSelection:YES];
3066 } else _assert(false);
3067 } else if (group-- == 0) {
3069 } else if (row-- == 0) {
3070 [cell setTitle:@"Identifier"];
3071 [cell setValue:[package_ id]];
3072 } else if (row-- == 0) {
3073 [cell setTitle:@"Section"];
3074 NSString *section([package_ section]);
3075 [cell setValue:(section == nil ? @"n/a" : section)];
3076 } else if ([package_ size] != 0 && row-- == 0) {
3077 [cell setTitle:@"Expanded Size"];
3078 [cell setValue:SizeString([package_ size])];
3079 } else if ([package_ maintainer] != nil && row-- == 0) {
3080 [cell setTitle:@"Maintainer"];
3081 [cell setValue:[[package_ maintainer] name]];
3082 [cell setShowDisclosure:YES];
3083 [cell setShowSelection:YES];
3084 } else if ([package_ sponsor] != nil && row-- == 0) {
3085 [cell setTitle:@"Sponsor"];
3086 [cell setValue:[[package_ sponsor] name]];
3087 [cell setShowDisclosure:YES];
3088 [cell setShowSelection:YES];
3089 } else if ([package_ relationships] != nil && row-- == 0) {
3090 [cell setTitle:@"Package Relationships"];
3091 [cell setShowDisclosure:YES];
3092 [cell setShowSelection:YES];
3093 } else if ([[package_ source] trusted] && row-- == 0) {
3094 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3095 [cell setValue:@"This package has been signed."];
3096 } else _assert(false);
3097 } else if ([package_ source] != nil && group-- == 0) {
3098 Source *source = [package_ source];
3099 NSString *description = [source description];
3102 } else if (row-- == 0) {
3103 NSString *label = [source label];
3105 label = [source uri];
3106 [cell setTitle:label];
3107 [cell setValue:[source version]];
3108 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3109 [cell setValue:description];
3110 } else if ([source origin] != nil && row-- == 0) {
3111 [cell setTitle:@"Origin"];
3112 [cell setValue:[source origin]];
3113 } else _assert(false);
3114 } else _assert(false);
3119 - (BOOL) canSelectRow:(int)row {
3123 - (void) tableRowSelected:(NSNotification *)notification {
3124 int row = [table_ selectedRow];
3128 #define _else else goto _label; return; } _label:
3132 } else if (row-- == 0) {
3133 } else if ([package_ author] != nil && row-- == 0) {
3134 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3135 [[package_ author] email],
3136 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3138 ] stringByAddingPercentEscapes]
3140 } else if (description_ != nil && row-- == 0) {
3141 } else if ([package_ website] != nil && row-- == 0) {
3142 NSURL *url = [NSURL URLWithString:[package_ website]];
3143 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3144 [browser setDelegate:delegate_];
3145 [book_ pushPage:browser];
3146 [browser loadURL:url];
3147 } _else if ([package_ installed] != nil) {
3149 } else if (row-- == 0) {
3150 } else if (row-- == 0) {
3151 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3152 [files setDelegate:delegate_];
3153 [files setPackage:package_];
3154 [book_ pushPage:files];
3157 } else if (row-- == 0) {
3158 } else if (row-- == 0) {
3159 } else if ([package_ size] != 0 && row-- == 0) {
3160 } else if ([package_ maintainer] != nil && row-- == 0) {
3161 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3162 [[package_ maintainer] email],
3163 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3165 ] stringByAddingPercentEscapes]
3167 } else if ([package_ sponsor] != nil && row-- == 0) {
3168 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3169 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3170 [browser setDelegate:delegate_];
3171 [book_ pushPage:browser];
3172 [browser loadURL:url];
3173 } else if ([package_ relationships] != nil && row-- == 0) {
3174 } else if ([[package_ source] trusted] && row-- == 0) {
3175 } _else if ([package_ source] != nil) {
3176 Source *source = [package_ source];
3177 NSString *description = [source description];
3180 } else if (row-- == 0) {
3181 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3182 } else if ([source origin] != nil && row-- == 0) {
3183 } _else _assert(false);
3188 - (void) _clickButtonWithName:(NSString *)name {
3189 if ([name isEqualToString:@"Install"])
3190 [delegate_ installPackage:package_];
3191 else if ([name isEqualToString:@"Reinstall"])
3192 [delegate_ installPackage:package_];
3193 else if ([name isEqualToString:@"Remove"])
3194 [delegate_ removePackage:package_];
3195 else if ([name isEqualToString:@"Upgrade"])
3196 [delegate_ installPackage:package_];
3197 else _assert(false);
3200 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3201 int count = [buttons_ count];
3202 _assert(count != 0);
3203 _assert(button <= count + 1);
3205 if (count != button - 1)
3206 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3211 - (void) _rightButtonClicked {
3212 int count = [buttons_ count];
3213 _assert(count != 0);
3216 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3218 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3219 [buttons addObjectsFromArray:buttons_];
3220 [buttons addObject:@"Cancel"];
3222 [delegate_ slideUp:[[[UIAlertSheet alloc]
3225 defaultButtonIndex:2
3232 - (NSString *) rightButtonTitle {
3233 int count = [buttons_ count];
3234 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3237 - (NSString *) title {
3241 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3242 if ((self = [super initWithBook:book]) != nil) {
3243 database_ = database;
3245 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3246 [self addSubview:table_];
3248 [table_ setDataSource:self];
3249 [table_ setDelegate:self];
3251 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3255 - (void) setPackage:(Package *)package {
3256 if (package_ != nil) {
3257 [package_ autorelease];
3266 if (description_ != nil) {
3267 [description_ release];
3271 [buttons_ removeAllObjects];
3273 if (package != nil) {
3274 package_ = [package retain];
3275 name_ = [[package id] retain];
3277 NSString *description([package description]);
3278 if (description == nil)
3279 description = [package tagline];
3280 if (description != nil) {
3281 description_ = [GetTextView(description, 12, true) retain];
3282 [description_ setTextColor:Black_];
3285 [table_ reloadData];
3287 if ([package_ source] == nil);
3288 else if ([package_ upgradableAndEssential:NO])
3289 [buttons_ addObject:@"Upgrade"];
3290 else if ([package_ installed] == nil)
3291 [buttons_ addObject:@"Install"];
3293 [buttons_ addObject:@"Reinstall"];
3294 if ([package_ installed] != nil)
3295 [buttons_ addObject:@"Remove"];
3299 - (void) resetViewAnimated:(BOOL)animated {
3300 [table_ resetViewAnimated:animated];
3303 - (void) reloadData {
3304 [self setPackage:[database_ packageWithName:name_]];
3305 [self reloadButtons];
3310 /* Package Table {{{ */
3311 @interface PackageTable : RVPage {
3312 _transient Database *database_;
3316 NSMutableArray *packages_;
3317 NSMutableArray *sections_;
3318 UISectionList *list_;
3321 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3323 - (void) setDelegate:(id)delegate;
3324 - (void) setObject:(id)object;
3326 - (void) reloadData;
3327 - (void) resetCursor;
3329 - (UISectionList *) list;
3331 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3335 @implementation PackageTable
3338 [list_ setDataSource:nil];
3343 [packages_ release];
3344 [sections_ release];
3349 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3350 return [sections_ count];
3353 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3354 return [[sections_ objectAtIndex:section] name];
3357 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3358 return [[sections_ objectAtIndex:section] row];
3361 - (int) numberOfRowsInTable:(UITable *)table {
3362 return [packages_ count];
3365 - (float) table:(UITable *)table heightForRow:(int)row {
3366 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3369 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3371 reusing = [[[PackageCell alloc] init] autorelease];
3372 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3376 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3380 - (void) tableRowSelected:(NSNotification *)notification {
3381 int row = [[notification object] selectedRow];
3385 Package *package = [packages_ objectAtIndex:row];
3386 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3387 [view setDelegate:delegate_];
3388 [view setPackage:package];
3389 [book_ pushPage:view];
3392 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3393 if ((self = [super initWithBook:book]) != nil) {
3394 database_ = database;
3395 title_ = [title retain];
3397 object_ = object == nil ? nil : [object retain];
3399 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3400 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3402 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3403 [list_ setDataSource:self];
3405 UITableColumn *column = [[[UITableColumn alloc]
3406 initWithTitle:@"Name"
3408 width:[self frame].size.width
3411 UITable *table = [list_ table];
3412 [table setSeparatorStyle:1];
3413 [table addTableColumn:column];
3414 [table setDelegate:self];
3415 [table setReusesTableCells:YES];
3417 [self addSubview:list_];
3422 - (void) setDelegate:(id)delegate {
3423 delegate_ = delegate;
3426 - (void) setObject:(id)object {
3432 object_ = [object retain];
3435 - (void) reloadData {
3436 NSArray *packages = [database_ packages];
3438 [packages_ removeAllObjects];
3439 [sections_ removeAllObjects];
3441 for (size_t i(0); i != [packages count]; ++i) {
3442 Package *package([packages objectAtIndex:i]);
3443 if ([[package performSelector:filter_ withObject:object_] boolValue])
3444 [packages_ addObject:package];
3447 Section *section = nil;
3449 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3450 Package *package = [packages_ objectAtIndex:offset];
3451 NSString *name = [package index];
3453 if (section == nil || ![[section name] isEqualToString:name]) {
3454 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3455 [sections_ addObject:section];
3458 [section addToCount];
3464 - (NSString *) title {
3468 - (void) resetViewAnimated:(BOOL)animated {
3469 [list_ resetViewAnimated:animated];
3472 - (void) resetCursor {
3473 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3476 - (UISectionList *) list {
3480 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3481 [list_ setShouldHideHeaderInShortLists:hide];
3487 /* Browser Implementation {{{ */
3488 @implementation BrowserView
3491 WebView *webview = [webview_ webView];
3492 [webview setFrameLoadDelegate:nil];
3493 [webview setResourceLoadDelegate:nil];
3494 [webview setUIDelegate:nil];
3496 [scroller_ setDelegate:nil];
3497 [webview_ setDelegate:nil];
3499 [scroller_ release];
3502 [indicator_ release];
3508 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3509 NSMutableURLRequest *request = [NSMutableURLRequest
3512 timeoutInterval:30.0
3515 [request addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3516 [request addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
3517 [request addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3519 [self loadRequest:request];
3523 - (void) loadURL:(NSURL *)url {
3524 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3527 // XXX: this needs to add the headers
3528 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3532 - (void) loadRequest:(NSURLRequest *)request {
3534 [webview_ loadRequest:request];
3537 - (void) reloadURL {
3538 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3539 [urls_ removeLastObject];
3540 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3543 - (WebView *) webView {
3544 return [webview_ webView];
3547 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3548 [scroller_ setContentSize:frame.size];
3551 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3552 [self view:sender didSetFrame:frame];
3555 - (void) getAppTapp:(NSString *)href {
3556 if ([href hasPrefix:@"apptapp://package/"]) {
3557 NSString *name = [href substringFromIndex:18];
3558 Package *package = [database_ packageWithName:name];
3559 if (package == nil) {
3560 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3561 initWithTitle:@"Cannot Locate Package"
3562 buttons:[NSArray arrayWithObjects:@"Close", nil]
3563 defaultButtonIndex:0
3568 [sheet setBodyText:[NSString stringWithFormat:
3569 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
3572 [sheet popupAlertAnimated:YES];
3574 [self setBackButtonTitle:title_];
3575 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3576 [view setDelegate:delegate_];
3577 [view setPackage:package];
3578 [book_ pushPage:view];
3583 - (void) webView:(WebView *)sender willClickElement:(id)element {
3584 if (![element respondsToSelector:@selector(href)])
3586 NSString *href = [element href];
3589 if ([href hasPrefix:@"apptapp://package/"])
3590 [self getAppTapp:href];
3593 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3594 if ([[[request URL] scheme] isEqualToString:@"apptapp"]) {
3595 [self getAppTapp:[[request URL] absoluteString]];
3601 [book_ pushPage:self];
3604 return [self _addHeadersToRequest:request];
3607 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3608 if (request != nil && [[[request URL] scheme] isEqualToString:@"apptapp"])
3611 [self setBackButtonTitle:title_];
3613 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3614 [browser setDelegate:delegate_];
3616 if (request != nil) {
3617 [browser loadRequest:[self _addHeadersToRequest:request]];
3618 [book_ pushPage:browser];
3621 return [browser webView];
3625 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3626 title_ = [title retain];
3627 [self setTitle:title];
3630 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3631 if ([frame parentFrame] != nil)
3636 [indicator_ startAnimation];
3637 [self reloadButtons];
3639 if (title_ != nil) {
3644 [self setTitle:@"Loading..."];
3646 WebView *webview = [webview_ webView];
3647 NSString *href = [webview mainFrameURL];
3648 [urls_ addObject:[NSURL URLWithString:href]];
3650 CGRect webrect = [scroller_ frame];
3651 webrect.size.height = 0;
3652 [webview_ setFrame:webrect];
3655 - (void) _finishLoading {
3658 [indicator_ stopAnimation];
3659 [self reloadButtons];
3663 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3664 if ([frame parentFrame] != nil)
3666 [self _finishLoading];
3669 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3670 if ([frame parentFrame] != nil)
3672 [self setTitle:[error localizedDescription]];
3673 [self _finishLoading];
3676 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3677 if ((self = [super initWithBook:book]) != nil) {
3678 database_ = database;
3681 struct CGRect bounds = [self bounds];
3683 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3684 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3685 [self addSubview:pinstripe];
3687 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3688 [self addSubview:scroller_];
3690 [scroller_ setScrollingEnabled:YES];
3691 [scroller_ setAdjustForContentSizeChange:YES];
3692 [scroller_ setClipsSubviews:YES];
3693 [scroller_ setAllowsRubberBanding:YES];
3694 [scroller_ setScrollDecelerationFactor:0.99];
3695 [scroller_ setDelegate:self];
3697 CGRect webrect = [scroller_ bounds];
3698 webrect.size.height = 0;
3700 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3701 [scroller_ addSubview:webview_];
3703 [webview_ setTilingEnabled:YES];
3704 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3705 [webview_ setAutoresizes:YES];
3706 [webview_ setDelegate:self];
3707 //[webview_ setEnabledGestures:2];
3709 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
3710 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
3711 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
3713 Package *package([database_ packageWithName:@"cydia"]);
3714 NSString *application = package == nil ? @"Cydia" : [NSString
3715 stringWithFormat:@"Cydia/%@",
3719 WebView *webview = [webview_ webView];
3720 [webview setApplicationNameForUserAgent:application];
3721 [webview setFrameLoadDelegate:self];
3722 [webview setResourceLoadDelegate:self];
3723 [webview setUIDelegate:self];
3725 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3729 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3733 - (void) _leftButtonClicked {
3734 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3735 initWithTitle:@"About Cydia Installer"
3736 buttons:[NSArray arrayWithObjects:@"Close", nil]
3737 defaultButtonIndex:0
3743 @"Copyright (C) 2008\n"
3744 "Jay Freeman (saurik)\n"
3745 "saurik@saurik.com\n"
3746 "http://www.saurik.com/\n"
3749 "http://www.theokorigroup.com/\n"
3751 "College of Creative Studies,\n"
3752 "University of California,\n"
3754 "http://www.ccs.ucsb.edu/"
3757 [sheet popupAlertAnimated:YES];
3760 - (void) _rightButtonClicked {
3765 - (NSString *) leftButtonTitle {
3769 - (NSString *) rightButtonTitle {
3770 return loading_ ? @"" : @"Reload";
3773 - (NSString *) title {
3777 - (NSString *) backButtonTitle {
3781 - (void) setPageActive:(BOOL)active {
3783 [book_ addSubview:indicator_];
3785 [indicator_ removeFromSuperview];
3788 - (void) resetViewAnimated:(BOOL)animated {
3791 - (void) setPushed:(bool)pushed {
3798 @interface CYBook : RVBook <
3801 _transient Database *database_;
3803 UIProgressIndicator *indicator_;
3804 UITextLabel *prompt_;
3805 UIProgressBar *progress_;
3809 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3815 /* Source Cell {{{ */
3816 @interface SourceCell : UITableCell {
3817 UITextLabel *description_;
3818 UIRightTextLabel *label_;
3819 UITextLabel *origin_;
3824 - (SourceCell *) initWithSource:(Source *)source;
3826 - (void) _setSelected:(float)fraction;
3827 - (void) setSelected:(BOOL)selected;
3828 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
3829 - (void) _setSelectionFadeFraction:(float)fraction;
3833 @implementation SourceCell
3836 [description_ release];
3842 - (SourceCell *) initWithSource:(Source *)source {
3843 if ((self = [super init]) != nil) {
3844 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
3845 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
3847 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
3848 float clear[] = {0, 0, 0, 0};
3850 NSString *description = [source description];
3851 if (description == nil)
3852 description = [source uri];
3854 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 270, 25)];
3855 [description_ setBackgroundColor:CGColorCreate(space, clear)];
3856 [description_ setFont:bold];
3857 [description_ setText:description];
3859 NSString *label = [source label];
3861 label = [source type];
3863 label_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 32, 90, 25)];
3864 [label_ setBackgroundColor:CGColorCreate(space, clear)];
3865 [label_ setFont:small];
3866 [label_ setText:label];
3868 NSString *origin = [source origin];
3870 origin = [source distribution];
3872 origin_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
3873 [origin_ setBackgroundColor:CGColorCreate(space, clear)];
3874 [origin_ setFont:small];
3875 [origin_ setText:origin];
3877 [self addSubview:description_];
3878 [self addSubview:label_];
3879 [self addSubview:origin_];
3886 - (void) _setSelected:(float)fraction {
3887 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
3890 Interpolate(0.0, 1.0, fraction),
3891 Interpolate(0.0, 1.0, fraction),
3892 Interpolate(0.0, 1.0, fraction),
3896 Interpolate(0.2, 1.0, fraction),
3897 Interpolate(0.2, 1.0, fraction),
3898 Interpolate(1.0, 1.0, fraction),
3902 Interpolate(0.4, 1.0, fraction),
3903 Interpolate(0.4, 1.0, fraction),
3904 Interpolate(0.4, 1.0, fraction),
3907 [description_ setColor:CGColorCreate(space, black)];
3908 [label_ setColor:CGColorCreate(space, blue)];
3909 [origin_ setColor:CGColorCreate(space, gray)];
3912 - (void) setSelected:(BOOL)selected {
3913 [self _setSelected:(selected ? 1.0 : 0.0)];
3914 [super setSelected:selected];
3917 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
3919 [self _setSelected:(selected ? 1.0 : 0.0)];
3920 [super setSelected:selected withFade:fade];
3923 - (void) _setSelectionFadeFraction:(float)fraction {
3924 [self _setSelected:fraction];
3925 [super _setSelectionFadeFraction:fraction];
3930 /* Source Table {{{ */
3931 @interface SourceTable : RVPage {
3932 _transient Database *database_;
3933 UISectionList *list_;
3934 NSMutableArray *sources_;
3935 UIAlertSheet *alert_;
3938 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3942 @implementation SourceTable
3945 [list_ setDataSource:nil];
3947 if (sources_ != nil)
3953 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3957 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3961 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3965 - (int) numberOfRowsInTable:(UITable *)table {
3966 return [sources_ count];
3969 - (float) table:(UITable *)table heightForRow:(int)row {
3973 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
3974 return [[[SourceCell alloc] initWithSource:[sources_ objectAtIndex:row]] autorelease];
3977 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3981 - (void) tableRowSelected:(NSNotification*)notification {
3982 UITable *table([list_ table]);
3983 int row([table selectedRow]);
3987 [table selectRow:-1 byExtendingSelection:NO withFade:YES];
3990 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3991 if ((self = [super initWithBook:book]) != nil) {
3992 database_ = database;
3995 list_ = [[UISectionList alloc] initWithFrame:[self bounds]];
3997 [self addSubview:list_];
3999 [list_ setDataSource:self];
4000 [list_ setShouldHideHeaderInShortLists:NO];
4002 UITableColumn *column = [[UITableColumn alloc]
4003 initWithTitle:@"Name"
4005 width:[self frame].size.width
4008 UITable *table = [list_ table];
4009 [table setSeparatorStyle:1];
4010 [table addTableColumn:column];
4011 [table setDelegate:self];
4015 - (void) reloadData {
4017 _assert(list.ReadMainList());
4019 if (sources_ != nil)
4022 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4023 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
4024 [sources_ addObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]];
4029 - (void) resetViewAnimated:(BOOL)animated {
4030 [list_ resetViewAnimated:animated];
4033 - (NSString *) leftTitle {
4034 return @"Refresh All";
4037 - (NSString *) rightTitle {
4044 /* Install View {{{ */
4045 @interface InstallView : RVPage {
4046 _transient Database *database_;
4047 NSMutableArray *packages_;
4048 NSMutableArray *sections_;
4053 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4054 - (void) reloadData;
4058 @implementation InstallView
4061 [list_ setDataSource:nil];
4062 [list_ setDelegate:nil];
4064 [packages_ release];
4065 [sections_ release];
4067 [accessory_ release];
4071 - (int) numberOfRowsInTable:(UITable *)table {
4072 return [sections_ count] + 1;
4075 - (float) table:(UITable *)table heightForRow:(int)row {
4079 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4081 reusing = [[[SectionCell alloc] init] autorelease];
4082 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
4086 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4090 - (void) tableRowSelected:(NSNotification *)notification {
4091 int row = [[notification object] selectedRow];
4102 title = @"All Packages";
4104 section = [sections_ objectAtIndex:(row - 1)];
4105 name = [section name];
4111 title = @"(No Section)";
4115 PackageTable *table = [[[PackageTable alloc]
4119 filter:@selector(isUninstalledInSection:)
4123 [table setDelegate:delegate_];
4125 [book_ pushPage:table];
4128 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4129 if ((self = [super initWithBook:book]) != nil) {
4130 database_ = database;
4132 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4133 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4135 list_ = [[UITable alloc] initWithFrame:[self bounds]];
4136 [self addSubview:list_];
4138 UITableColumn *column = [[[UITableColumn alloc]
4139 initWithTitle:@"Name"
4141 width:[self frame].size.width
4144 [list_ setDataSource:self];
4145 [list_ setSeparatorStyle:1];
4146 [list_ addTableColumn:column];
4147 [list_ setDelegate:self];
4148 [list_ setReusesTableCells:YES];
4154 - (void) reloadData {
4155 NSArray *packages = [database_ packages];
4157 [packages_ removeAllObjects];
4158 [sections_ removeAllObjects];
4160 for (size_t i(0); i != [packages count]; ++i) {
4161 Package *package([packages objectAtIndex:i]);
4162 if ([package valid] && [package installed] == nil)
4163 [packages_ addObject:package];
4166 [packages_ sortUsingSelector:@selector(compareBySection:)];
4168 Section *section = nil;
4169 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4170 Package *package = [packages_ objectAtIndex:offset];
4171 NSString *name = [package section];
4173 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4174 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4175 [sections_ addObject:section];
4178 [section addToCount];
4184 - (void) resetViewAnimated:(BOOL)animated {
4185 [list_ resetViewAnimated:animated];
4188 - (NSString *) title {
4192 - (NSString *) backButtonTitle {
4196 - (UIView *) accessoryView {
4202 /* Changes View {{{ */
4203 @interface ChangesView : RVPage {
4204 _transient Database *database_;
4205 NSMutableArray *packages_;
4206 NSMutableArray *sections_;
4207 UISectionList *list_;
4211 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4212 - (void) reloadData;
4216 @implementation ChangesView
4219 [[list_ table] setDelegate:nil];
4220 [list_ setDataSource:nil];
4222 [packages_ release];
4223 [sections_ release];
4228 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4229 return [sections_ count];
4232 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4233 return [[sections_ objectAtIndex:section] name];
4236 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4237 return [[sections_ objectAtIndex:section] row];
4240 - (int) numberOfRowsInTable:(UITable *)table {
4241 return [packages_ count];
4244 - (float) table:(UITable *)table heightForRow:(int)row {
4245 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4248 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4250 reusing = [[[PackageCell alloc] init] autorelease];
4251 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4255 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4259 - (void) tableRowSelected:(NSNotification *)notification {
4260 int row = [[notification object] selectedRow];
4263 Package *package = [packages_ objectAtIndex:row];
4264 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4265 [view setDelegate:delegate_];
4266 [view setPackage:package];
4267 [book_ pushPage:view];
4270 - (void) _leftButtonClicked {
4271 [(CYBook *)book_ update];
4272 [self reloadButtons];
4275 - (void) _rightButtonClicked {
4276 [delegate_ distUpgrade];
4279 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4280 if ((self = [super initWithBook:book]) != nil) {
4281 database_ = database;
4283 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4284 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4286 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4287 [self addSubview:list_];
4289 [list_ setShouldHideHeaderInShortLists:NO];
4290 [list_ setDataSource:self];
4291 //[list_ setSectionListStyle:1];
4293 UITableColumn *column = [[[UITableColumn alloc]
4294 initWithTitle:@"Name"
4296 width:[self frame].size.width
4299 UITable *table = [list_ table];
4300 [table setSeparatorStyle:1];
4301 [table addTableColumn:column];
4302 [table setDelegate:self];
4303 [table setReusesTableCells:YES];
4309 - (void) reloadData {
4310 NSArray *packages = [database_ packages];
4312 [packages_ removeAllObjects];
4313 [sections_ removeAllObjects];
4315 for (size_t i(0); i != [packages count]; ++i) {
4316 Package *package([packages objectAtIndex:i]);
4317 if ([package installed] == nil && [package valid] || [package upgradableAndEssential:NO])
4318 [packages_ addObject:package];
4321 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4323 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
4324 Section *section = nil;
4327 bool unseens = false;
4329 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4331 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4332 Package *package = [packages_ objectAtIndex:offset];
4334 if ([package upgradableAndEssential:YES]) {
4336 [upgradable addToCount];
4339 NSDate *seen = [package seen];
4344 name = [@"n/a ?" retain];
4346 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4349 if (section == nil || ![[section name] isEqualToString:name]) {
4350 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4351 [sections_ addObject:section];
4355 [section addToCount];
4359 CFRelease(formatter);
4362 Section *last = [sections_ lastObject];
4363 size_t count = [last count];
4364 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4365 [sections_ removeLastObject];
4369 [sections_ insertObject:upgradable atIndex:0];
4372 [self reloadButtons];
4375 - (void) resetViewAnimated:(BOOL)animated {
4376 [list_ resetViewAnimated:animated];
4379 - (NSString *) leftButtonTitle {
4380 return [(CYBook *)book_ updating] ? nil : @"Refresh";
4383 - (NSString *) rightButtonTitle {
4384 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
4387 - (NSString *) title {
4393 /* Manage View {{{ */
4394 @interface ManageView : RVPage {
4395 _transient Database *database_;
4396 PackageTable *packages_;
4397 SourceTable *sources_;
4400 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4404 @implementation ManageView
4407 [packages_ release];
4412 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4413 if ((self = [super initWithBook:book]) != nil) {
4414 database_ = database;
4416 packages_ = [[PackageTable alloc]
4420 filter:@selector(isInstalledInSection:)
4424 sources_ = [[SourceTable alloc]
4429 [self addSubview:packages_];
4433 - (void) resetViewAnimated:(BOOL)animated {
4434 [packages_ resetViewAnimated:animated];
4435 [sources_ resetViewAnimated:animated];
4438 - (void) reloadData {
4439 [packages_ reloadData];
4440 [sources_ reloadData];
4443 - (NSString *) title {
4444 return @"Installed Packages";
4447 - (NSString *) backButtonTitle {
4451 - (void) setDelegate:(id)delegate {
4452 [super setDelegate:delegate];
4453 [packages_ setDelegate:delegate];
4454 [sources_ setDelegate:delegate];
4459 /* Search View {{{ */
4460 @protocol SearchViewDelegate
4461 - (void) showKeyboard:(BOOL)show;
4464 @interface SearchView : RVPage {
4466 UISearchField *field_;
4467 UITransitionView *transition_;
4468 PackageTable *table_;
4469 UIPreferencesTable *advanced_;
4475 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4476 - (void) reloadData;
4480 @implementation SearchView
4484 [[field_ textTraits] setEditingDelegate:nil];
4486 [field_ setDelegate:nil];
4488 [accessory_ release];
4490 [transition_ release];
4492 [advanced_ release];
4497 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4501 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4503 case 0: return @"Advanced Search (Coming Soon!)";
4505 default: _assert(false);
4509 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4513 default: _assert(false);
4517 - (void) _showKeyboard:(BOOL)show {
4518 CGSize keysize = [UIKeyboard defaultSize];
4519 CGRect keydown = [book_ pageBounds];
4520 CGRect keyup = keydown;
4521 keyup.size.height -= keysize.height - ButtonBarHeight_;
4523 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4525 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4526 [animation setSignificantRectFields:8];
4529 [animation setStartFrame:keydown];
4530 [animation setEndFrame:keyup];
4532 [animation setStartFrame:keyup];
4533 [animation setEndFrame:keydown];
4536 UIAnimator *animator = [UIAnimator sharedAnimator];
4539 addAnimations:[NSArray arrayWithObjects:animation, nil]
4540 withDuration:(KeyboardTime_ - delay)
4545 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4548 [delegate_ showKeyboard:show];
4552 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
4553 [self _showKeyboard:YES];
4556 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
4557 [self _showKeyboard:NO];
4560 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
4562 NSString *text([field_ text]);
4563 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
4569 - (void) textFieldClearButtonPressed:(UITextField *)field {
4573 - (void) keyboardInputShouldDelete:(id)input {
4577 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
4578 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
4582 [field_ resignFirstResponder];
4587 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4588 if ((self = [super initWithBook:book]) != nil) {
4589 CGRect pageBounds = [book_ pageBounds];
4591 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
4592 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4593 [self addSubview:pinstripe];*/
4595 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
4596 [self addSubview:transition_];
4598 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
4600 [advanced_ setReusesTableCells:YES];
4601 [advanced_ setDataSource:self];
4602 [advanced_ reloadData];
4604 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
4605 CGColor dimmed(space_, 0, 0, 0, 0.5);
4606 [dimmed_ setBackgroundColor:dimmed];
4608 table_ = [[PackageTable alloc]
4612 filter:@selector(isSearchedForBy:)
4616 [table_ setShouldHideHeaderInShortLists:NO];
4617 [transition_ transition:0 toView:table_];
4626 area.origin.x = cnfrect.origin.x + cnfrect.size.width + 14;
4633 [self bounds].size.width - area.origin.x - 18;
4635 area.size.height = [UISearchField defaultHeight];
4637 field_ = [[UISearchField alloc] initWithFrame:area];
4639 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
4640 [field_ setFont:font];
4643 [field_ setPlaceholder:@"Package Names & Descriptions"];
4644 [field_ setDelegate:self];
4647 [field_ setPaddingTop:3];
4649 [field_ setPaddingTop:5];
4653 UITextTraits *traits = [field_ textTraits];
4654 [traits setEditingDelegate:self];
4655 [traits setReturnKeyType:6];
4656 [traits setAutoCapsType:0];
4657 [traits setAutoCorrectionType:1];
4660 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
4662 accessory_ = [[UIView alloc] initWithFrame:accrect];
4663 [accessory_ addSubview:field_];
4665 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
4666 [configure setShowPressFeedback:YES];
4667 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
4668 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
4669 [accessory_ addSubview:configure];
4675 LKAnimation *animation = [LKTransition animation];
4676 [animation setType:@"oglFlip"];
4677 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
4678 [animation setFillMode:@"extended"];
4679 [animation setTransitionFlags:3];
4680 [animation setDuration:10];
4681 [animation setSpeed:0.35];
4682 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
4683 [[transition_ _layer] addAnimation:animation forKey:0];
4684 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
4685 flipped_ = !flipped_;
4689 - (void) configurePushed {
4690 [field_ resignFirstResponder];
4694 - (void) resetViewAnimated:(BOOL)animated {
4697 [table_ resetViewAnimated:animated];
4700 - (void) reloadData {
4703 [table_ setObject:[field_ text]];
4704 [table_ reloadData];
4705 [table_ resetCursor];
4708 - (UIView *) accessoryView {
4712 - (NSString *) title {
4716 - (NSString *) backButtonTitle {
4720 - (void) setDelegate:(id)delegate {
4721 [table_ setDelegate:delegate];
4722 [super setDelegate:delegate];
4728 @implementation CYBook
4732 [indicator_ release];
4734 [progress_ release];
4738 - (NSString *) getTitleForPage:(RVPage *)page {
4739 return Simplify([super getTitleForPage:page]);
4747 [navbar_ setPrompt:@""];
4748 [navbar_ addSubview:overlay_];
4749 [indicator_ startAnimation];
4750 [prompt_ setText:@"Updating Database..."];
4751 [progress_ setProgress:0];
4756 detachNewThreadSelector:@selector(_update)
4765 [overlay_ removeFromSuperview];
4766 [indicator_ stopAnimation];
4767 [delegate_ reloadData];
4769 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
4772 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4773 if ((self = [super initWithFrame:frame]) != nil) {
4774 database_ = database;
4777 [navbar_ setBarStyle:1];
4779 CGRect ovrrect = [navbar_ bounds];
4780 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
4782 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
4784 UIProgressIndicatorStyle style = Advanced_ ?
4785 kUIProgressIndicatorStyleSmallWhite :
4786 kUIProgressIndicatorStyleSmallBlack;
4788 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
4789 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4790 CGRect indrect = {{indoffset, indoffset}, indsize};
4792 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4793 [indicator_ setStyle:style];
4794 [overlay_ addSubview:indicator_];
4796 CGSize prmsize = {200, indsize.width + 4};
4799 indoffset * 2 + indsize.width,
4803 (ovrrect.size.height - prmsize.height) / 2
4806 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
4808 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4810 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
4811 [prompt_ setBackgroundColor:Clear_];
4812 [prompt_ setFont:font];
4816 [overlay_ addSubview:prompt_];
4818 CGSize prgsize = {75, 100};
4821 ovrrect.size.width - prgsize.width - 10,
4822 (ovrrect.size.height - prgsize.height) / 2
4825 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4826 [progress_ setStyle:0];
4827 [overlay_ addSubview:progress_];
4832 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4835 status.setDelegate(self);
4837 [database_ updateWithStatus:status];
4840 performSelectorOnMainThread:@selector(_update_)
4848 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
4849 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4852 - (void) setProgressTitle:(NSString *)title {
4854 performSelectorOnMainThread:@selector(_setProgressTitle:)
4860 - (void) setProgressPercent:(float)percent {
4863 - (void) addProgressOutput:(NSString *)output {
4865 performSelectorOnMainThread:@selector(_addProgressOutput:)
4871 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4875 - (void) _setProgressTitle:(NSString *)title {
4876 [prompt_ setText:[title stringByAppendingString:@"..."]];
4879 - (void) _addProgressOutput:(NSString *)output {
4884 @interface Cydia : UIApplication <
4885 ConfirmationViewDelegate,
4886 ProgressViewDelegate,
4895 UIButtonBar *buttonbar_;
4897 ConfirmationView *confirm_;
4899 NSMutableArray *essential_;
4900 NSMutableArray *broken_;
4902 Database *database_;
4903 ProgressView *progress_;
4907 UIKeyboard *keyboard_;
4908 UIProgressHUD *hud_;
4910 InstallView *install_;
4911 ChangesView *changes_;
4912 ManageView *manage_;
4913 SearchView *search_;
4918 @implementation Cydia
4921 if ([broken_ count] != 0) {
4922 int count = [broken_ count];
4924 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4925 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
4926 buttons:[NSArray arrayWithObjects:
4928 @"Ignore (Temporary)",
4930 defaultButtonIndex:0
4935 [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."];
4936 [sheet popupAlertAnimated:YES];
4937 } else if (!Ignored_ && [essential_ count] != 0) {
4938 int count = [essential_ count];
4940 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4941 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
4942 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
4943 defaultButtonIndex:0
4948 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
4949 [sheet popupAlertAnimated:YES];
4953 - (void) _reloadData {
4954 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4955 [hud setText:@"Reloading Data"];
4956 [overlay_ addSubview:hud];
4959 [database_ reloadData];
4961 if (Packages_ == nil) {
4962 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:128];
4963 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4968 [essential_ removeAllObjects];
4969 [broken_ removeAllObjects];
4971 NSArray *packages = [database_ packages];
4972 for (int i(0), e([packages count]); i != e; ++i) {
4973 Package *package = [packages objectAtIndex:i];
4975 [broken_ addObject:package];
4976 if ([package upgradableAndEssential:NO]) {
4977 if ([package essential])
4978 [essential_ addObject:package];
4984 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4985 [buttonbar_ setBadgeValue:badge forButton:3];
4986 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4987 [buttonbar_ setBadgeAnimated:YES forButton:3];
4988 [self setApplicationBadge:badge];
4990 [buttonbar_ setBadgeValue:nil forButton:3];
4991 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4992 [buttonbar_ setBadgeAnimated:NO forButton:3];
4993 [self removeApplicationBadge];
4997 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5001 /* XXX: this is just stupid */
5003 [install_ reloadData];
5005 [changes_ reloadData];
5007 [manage_ reloadData];
5009 [search_ reloadData];
5013 if ([packages count] == 0);
5022 [hud removeFromSuperview];*/
5025 - (void) reloadData {
5026 @synchronized (self) {
5027 if (confirm_ == nil)
5033 pkgProblemResolver *resolver = [database_ resolver];
5035 resolver->InstallProtect();
5036 if (!resolver->Resolve(true))
5041 [database_ prepare];
5043 if ([database_ cache]->BrokenCount() == 0)
5044 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5046 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5047 NSArray *packages = [database_ packages];
5049 for (size_t i(0); i != [packages count]; ++i) {
5050 Package *package = [packages objectAtIndex:i];
5051 if ([package broken])
5052 [broken addObject:[package name]];
5055 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5056 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5057 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5058 defaultButtonIndex:0
5063 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5064 [sheet popupAlertAnimated:YES];
5070 - (void) installPackage:(Package *)package {
5071 @synchronized (self) {
5078 - (void) removePackage:(Package *)package {
5079 @synchronized (self) {
5086 - (void) distUpgrade {
5087 @synchronized (self) {
5088 [database_ upgrade];
5094 @synchronized (self) {
5102 [overlay_ removeFromSuperview];
5106 detachNewThreadSelector:@selector(perform)
5113 - (void) bootstrap_ {
5115 [database_ upgrade];
5116 [database_ prepare];
5117 [database_ perform];
5120 - (void) bootstrap {
5122 detachNewThreadSelector:@selector(bootstrap_)
5125 title:@"Bootstrap Install..."
5129 - (void) progressViewIsComplete:(ProgressView *)progress {
5130 @synchronized (self) {
5133 if (confirm_ != nil) {
5134 [underlay_ addSubview:overlay_];
5135 [confirm_ removeFromSuperview];
5142 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5143 NSString *context = [sheet context];
5144 if ([context isEqualToString:@"fixhalf"])
5147 @synchronized (self) {
5148 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5149 Package *broken = [broken_ objectAtIndex:i];
5152 NSString *id = [broken id];
5153 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5154 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5155 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5156 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5165 [broken_ removeAllObjects];
5172 else if ([context isEqualToString:@"upgrade"])
5175 @synchronized (self) {
5176 for (int i = 0, e = [essential_ count]; i != e; ++i) {
5177 Package *essential = [essential_ objectAtIndex:i];
5178 [essential install];
5197 - (void) setPage:(RVPage *)page {
5198 [page resetViewAnimated:NO];
5199 [page setDelegate:self];
5200 [book_ setPage:page];
5203 - (RVPage *) _setHomePage {
5204 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
5205 [self setPage:browser];
5206 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
5210 - (void) buttonBarItemTapped:(id)sender {
5211 unsigned tag = [sender tag];
5213 [book_ resetViewAnimated:YES];
5218 case 1: [self _setHomePage]; break;
5220 case 2: [self setPage:install_]; break;
5221 case 3: [self setPage:changes_]; break;
5222 case 4: [self setPage:manage_]; break;
5223 case 5: [self setPage:search_]; break;
5225 default: _assert(false);
5231 - (void) applicationWillSuspend {
5233 [super applicationWillSuspend];
5238 pid_t pid = ExecFork();
5244 if (pid_t child = fork()) {
5245 waitpid(child, NULL, 0);
5247 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5248 perror("launchctl unload");
5252 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5253 perror("launchctl load");
5261 [self setStatusBarShowsProgress:NO];
5264 [hud_ removeFromSuperview];
5269 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5271 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5272 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5273 0, 0, screenrect.size.width, screenrect.size.height - 48
5274 ) database:database_];
5276 [book_ setDelegate:self];
5278 [overlay_ addSubview:book_];
5280 NSArray *buttonitems = [NSArray arrayWithObjects:
5281 [NSDictionary dictionaryWithObjectsAndKeys:
5282 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5283 @"home-up.png", kUIButtonBarButtonInfo,
5284 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5285 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5286 self, kUIButtonBarButtonTarget,
5287 @"Home", kUIButtonBarButtonTitle,
5288 @"0", kUIButtonBarButtonType,
5291 [NSDictionary dictionaryWithObjectsAndKeys:
5292 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5293 @"install-up.png", kUIButtonBarButtonInfo,
5294 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5295 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5296 self, kUIButtonBarButtonTarget,
5297 @"Install", kUIButtonBarButtonTitle,
5298 @"0", kUIButtonBarButtonType,
5301 [NSDictionary dictionaryWithObjectsAndKeys:
5302 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5303 @"changes-up.png", kUIButtonBarButtonInfo,
5304 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5305 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5306 self, kUIButtonBarButtonTarget,
5307 @"Changes", kUIButtonBarButtonTitle,
5308 @"0", kUIButtonBarButtonType,
5311 [NSDictionary dictionaryWithObjectsAndKeys:
5312 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5313 @"manage-up.png", kUIButtonBarButtonInfo,
5314 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5315 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5316 self, kUIButtonBarButtonTarget,
5317 @"Manage", kUIButtonBarButtonTitle,
5318 @"0", kUIButtonBarButtonType,
5321 [NSDictionary dictionaryWithObjectsAndKeys:
5322 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5323 @"search-up.png", kUIButtonBarButtonInfo,
5324 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5325 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5326 self, kUIButtonBarButtonTarget,
5327 @"Search", kUIButtonBarButtonTitle,
5328 @"0", kUIButtonBarButtonType,
5332 buttonbar_ = [[UIButtonBar alloc]
5334 withFrame:CGRectMake(
5335 0, screenrect.size.height - ButtonBarHeight_,
5336 screenrect.size.width, ButtonBarHeight_
5338 withItemList:buttonitems
5341 [buttonbar_ setDelegate:self];
5342 [buttonbar_ setBarStyle:1];
5343 [buttonbar_ setButtonBarTrackingMode:2];
5345 int buttons[5] = {1, 2, 3, 4, 5};
5346 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5347 [buttonbar_ showButtonGroup:0 withDuration:0];
5349 for (int i = 0; i != 5; ++i)
5350 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5351 i * 64 + 2, 1, 60, ButtonBarHeight_
5354 [buttonbar_ showSelectionForButton:1];
5355 [overlay_ addSubview:buttonbar_];
5357 [UIKeyboard initImplementationNow];
5358 CGSize keysize = [UIKeyboard defaultSize];
5359 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5360 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5361 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5362 [overlay_ addSubview:keyboard_];
5364 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5365 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5366 manage_ = [[ManageView alloc] initWithBook:book_ database:database_];
5367 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5370 [underlay_ addSubview:overlay_];
5377 [self _setHomePage];
5380 - (void) reorganize {
5381 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5382 //system("/usr/libexec/cydia/free.sh");
5383 //[self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
5387 - (void) applicationSuspend:(__GSEvent *)event {
5388 if (hud_ == nil && ![progress_ isRunning])
5389 [super applicationSuspend:event];
5393 - (void) applicationDidFinishLaunching:(id)unused {
5394 _assert(pkgInitConfig(*_config));
5395 _assert(pkgInitSystem(*_config, _system));
5400 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
5401 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
5403 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5404 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
5406 [window_ orderFront: self];
5407 [window_ makeKey: self];
5408 [window_ _setHidden: NO];
5410 database_ = [[Database alloc] init];
5411 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
5412 [database_ setDelegate:progress_];
5413 [window_ setContentView:progress_];
5415 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
5416 [progress_ setContentView:underlay_];
5418 [progress_ resetView];
5421 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
5422 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL ||
5423 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
5424 readlink("/Library/Wallpapers", NULL, 0) == -1 && errno == EINVAL
5426 hud_ = [[UIProgressHUD alloc] initWithWindow:window_];
5427 [hud_ setText:@"Reorganizing\nOne Minute!"];
5429 [underlay_ addSubview:hud_];
5431 [self setStatusBarShowsProgress:YES];
5434 detachNewThreadSelector:@selector(reorganize)
5442 - (void) showKeyboard:(BOOL)show {
5443 CGSize keysize = [UIKeyboard defaultSize];
5444 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
5445 CGRect keyup = keydown;
5446 keyup.origin.y -= keysize.height;
5448 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
5449 [animation setSignificantRectFields:2];
5452 [animation setStartFrame:keydown];
5453 [animation setEndFrame:keyup];
5454 [keyboard_ activate];
5456 [animation setStartFrame:keyup];
5457 [animation setEndFrame:keydown];
5458 [keyboard_ deactivate];
5461 [[UIAnimator sharedAnimator]
5462 addAnimations:[NSArray arrayWithObjects:animation, nil]
5463 withDuration:KeyboardTime_
5468 - (void) slideUp:(UIAlertSheet *)alert {
5470 [alert presentSheetFromButtonBar:buttonbar_];
5472 [alert presentSheetInView:overlay_];
5477 void AddPreferences(NSString *plist) {
5478 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5480 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
5481 _assert(settings != NULL);
5482 NSMutableArray *items = [settings objectForKey:@"items"];
5486 for (size_t i(0); i != [items count]; ++i) {
5487 NSMutableDictionary *item([items objectAtIndex:i]);
5488 NSString *label = [item objectForKey:@"label"];
5489 if (label != nil && [label isEqualToString:@"Cydia"]) {
5496 for (size_t i(0); i != [items count]; ++i) {
5497 NSDictionary *item([items objectAtIndex:i]);
5498 NSString *label = [item objectForKey:@"label"];
5499 if (label != nil && [label isEqualToString:@"General"]) {
5500 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
5501 @"CydiaSettings", @"bundle",
5502 @"PSLinkCell", @"cell",
5503 [NSNumber numberWithBool:YES], @"hasIcon",
5504 [NSNumber numberWithBool:YES], @"isController",
5506 nil] atIndex:(i + 1)];
5512 _assert([settings writeToFile:plist atomically:YES] == YES);
5519 id Alloc_(id self, SEL selector) {
5520 id object = alloc_(self, selector);
5521 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
5526 id Dealloc_(id self, SEL selector) {
5527 id object = dealloc_(self, selector);
5528 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
5532 int main(int argc, char *argv[]) {
5533 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5535 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
5537 Home_ = NSHomeDirectory();
5540 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
5541 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
5542 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
5543 Sounds_Keyboard_ = [keyboard boolValue];
5549 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
5550 _assert(errno == ENOENT);
5551 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
5552 _assert(errno == ENOENT);
5554 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
5555 alloc_ = alloc->method_imp;
5556 alloc->method_imp = (IMP) &Alloc_;*/
5558 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
5559 dealloc_ = dealloc->method_imp;
5560 dealloc->method_imp = (IMP) &Dealloc_;*/
5562 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
5563 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5564 Firmware_ = strdup([prover UTF8String]);
5565 NSArray *versions = [prover componentsSeparatedByString:@"."];
5566 int count = [versions count];
5567 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
5568 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
5569 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
5574 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
5575 char *machine = new char[size];
5576 sysctlbyname("hw.machine", machine, &size, NULL, 0);
5579 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
5580 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
5581 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
5582 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
5586 IOObjectRelease(service);
5589 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
5590 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
5592 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
5593 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
5595 Packages_ = [Metadata_ objectForKey:@"Packages"];
5597 if (access("/User", F_OK) != 0)
5598 system("/usr/libexec/cydia/firmware.sh");
5600 Locale_ = CFLocaleCopyCurrent();
5601 space_ = CGColorSpaceCreateDeviceRGB();
5603 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
5604 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
5605 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
5606 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
5607 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
5609 int value = UIApplicationMain(argc, argv, [Cydia class]);
5611 CGColorSpaceRelease(space_);