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]];
138 @interface NSString (UIKit)
139 - (NSString *) stringByAddingPercentEscapes;
140 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
143 @interface NSString (Cydia)
144 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
145 - (NSComparisonResult) compareByPath:(NSString *)other;
148 @implementation NSString (Cydia)
150 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
151 char data[length + 1];
152 memcpy(data, bytes, length);
154 return [NSString stringWithUTF8String:data];
157 - (NSComparisonResult) compareByPath:(NSString *)other {
158 NSString *prefix = [self commonPrefixWithString:other options:0];
159 size_t length = [prefix length];
161 NSRange lrange = NSMakeRange(length, [self length] - length);
162 NSRange rrange = NSMakeRange(length, [other length] - length);
164 lrange = [self rangeOfString:@"/" options:0 range:lrange];
165 rrange = [other rangeOfString:@"/" options:0 range:rrange];
167 NSComparisonResult value;
169 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
170 value = NSOrderedSame;
171 else if (lrange.location == NSNotFound)
172 value = NSOrderedAscending;
173 else if (rrange.location == NSNotFound)
174 value = NSOrderedDescending;
176 value = NSOrderedSame;
178 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
179 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
180 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
181 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
183 NSComparisonResult result = [lpath compare:rpath];
184 return result == NSOrderedSame ? value : result;
189 /* Perl-Compatible RegEx {{{ */
199 Pcre(const char *regex) :
204 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
207 fprintf(stderr, "%d:%s\n", offset, error);
211 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
212 matches_ = new int[(capture_ + 1) * 3];
220 NSString *operator [](size_t match) {
221 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
224 bool operator ()(NSString *data) {
225 // XXX: length is for characters, not for bytes
226 return operator ()([data UTF8String], [data length]);
229 bool operator ()(const char *data, size_t size) {
231 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
235 /* Mime Addresses {{{ */
236 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
238 @interface Address : NSObject {
244 - (NSString *) email;
246 + (Address *) addressWithString:(NSString *)string;
247 - (Address *) initWithString:(NSString *)string;
250 @implementation Address
259 - (NSString *) name {
263 - (NSString *) email {
267 + (Address *) addressWithString:(NSString *)string {
268 return [[[Address alloc] initWithString:string] autorelease];
271 - (Address *) initWithString:(NSString *)string {
272 if ((self = [super init]) != nil) {
273 const char *data = [string UTF8String];
274 size_t size = [string length];
276 if (email_r(data, size)) {
277 name_ = [email_r[1] retain];
278 email_ = [email_r[2] retain];
280 name_ = [[NSString alloc]
283 encoding:kCFStringEncodingUTF8
293 /* CoreGraphics Primitives {{{ */
304 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
307 Set(space, red, green, blue, alpha);
312 CGColorRelease(color_);
319 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
321 float color[] = {red, green, blue, alpha};
322 color_ = CGColorCreate(space, color);
325 operator CGColorRef() {
340 /* Right Alignment {{{ */
341 @interface UIRightTextLabel : UITextLabel {
342 float _savedRightEdgeX;
343 BOOL _sizedtofit_flag;
346 - (void) setFrame:(CGRect)frame;
347 - (void) setText:(NSString *)text;
348 - (void) realignText;
351 @implementation UIRightTextLabel
353 - (void) setFrame:(CGRect)frame {
354 [super setFrame:frame];
355 if (_sizedtofit_flag == NO) {
356 _savedRightEdgeX = frame.origin.x;
361 - (void) setText:(NSString *)text {
362 [super setText:text];
366 - (void) realignText {
367 CGRect oldFrame = [self frame];
369 _sizedtofit_flag = YES;
370 [self sizeToFit]; // shrink down size so I can right align it
372 CGRect newFrame = [self frame];
374 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
375 oldFrame.size.width = newFrame.size.width;
376 [super setFrame:oldFrame];
377 _sizedtofit_flag = NO;
383 /* Random Global Variables {{{ */
384 static const int PulseInterval_ = 50000;
385 static const int ButtonBarHeight_ = 48;
386 static const float KeyboardTime_ = 0.4f;
392 static CGColor Blueish_;
393 static CGColor Black_;
394 static CGColor Clear_;
396 static CGColor White_;
398 static NSString *Home_;
399 static BOOL Sounds_Keyboard_;
401 static BOOL Advanced_;
403 static BOOL Ignored_;
405 const char *Firmware_ = NULL;
406 const char *Machine_ = NULL;
407 const char *SerialNumber_ = NULL;
413 CGColorSpaceRef space_;
415 #define FW_LEAST(major, minor, bugfix) \
416 (major < Major_ || major == Major_ && \
417 (minor < Minor_ || minor == Minor_ && \
423 static NSMutableDictionary *Metadata_;
424 static NSMutableDictionary *Packages_;
425 static bool Changed_;
428 NSString *GetLastUpdate() {
429 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
432 return @"Never or Unknown";
434 CFLocaleRef locale = CFLocaleCopyCurrent();
435 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
436 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
438 CFRelease(formatter);
441 return [(NSString *) formatted autorelease];
444 /* Display Helpers {{{ */
445 inline float Interpolate(float begin, float end, float fraction) {
446 return (end - begin) * fraction + begin;
449 NSString *SizeString(double size) {
451 while (size > 1024) {
456 static const char *powers_[] = {"B", "kB", "MB", "GB"};
458 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
461 static const float TextViewOffset_ = 22;
463 UITextView *GetTextView(NSString *value, float left, bool html) {
464 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
465 [text setEditable:NO];
466 [text setTextSize:16];
468 [text setHTML:value];
470 [text setText:value];
471 [text setEnabled:NO];
473 [text setBackgroundColor:Clear_];
475 CGRect frame = [text frame];
476 [text setFrame:frame];
477 CGRect rect = [text visibleTextRect];
478 frame.size.height = rect.size.height;
479 [text setFrame:frame];
484 NSString *Simplify(NSString *title) {
485 const char *data = [title UTF8String];
486 size_t size = [title length];
488 Pcre title_r("^(.*?)( \\(.*\\))?$");
489 if (title_r(data, size))
496 /* Delegate Prototypes {{{ */
500 @protocol ProgressDelegate
501 - (void) setProgressError:(NSString *)error;
502 - (void) setProgressTitle:(NSString *)title;
503 - (void) setProgressPercent:(float)percent;
504 - (void) addProgressOutput:(NSString *)output;
507 @protocol ConfigurationDelegate
508 - (void) repairWithSelector:(SEL)selector;
509 - (void) setConfigurationData:(NSString *)data;
512 @protocol CydiaDelegate
513 - (void) installPackage:(Package *)package;
514 - (void) removePackage:(Package *)package;
515 - (void) slideUp:(UIAlertSheet *)alert;
516 - (void) distUpgrade;
520 /* Status Delegation {{{ */
522 public pkgAcquireStatus
525 _transient id<ProgressDelegate> delegate_;
533 void setDelegate(id delegate) {
534 delegate_ = delegate;
537 virtual bool MediaChange(std::string media, std::string drive) {
541 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
544 virtual void Fetch(pkgAcquire::ItemDesc &item) {
545 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
548 virtual void Done(pkgAcquire::ItemDesc &item) {
551 virtual void Fail(pkgAcquire::ItemDesc &item) {
553 item.Owner->Status == pkgAcquire::Item::StatIdle ||
554 item.Owner->Status == pkgAcquire::Item::StatDone
558 [delegate_ setProgressError:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()]];
561 virtual bool Pulse(pkgAcquire *Owner) {
562 bool value = pkgAcquireStatus::Pulse(Owner);
565 double(CurrentBytes + CurrentItems) /
566 double(TotalBytes + TotalItems)
569 [delegate_ setProgressPercent:percent];
573 virtual void Start() {
576 virtual void Stop() {
580 /* Progress Delegation {{{ */
585 _transient id<ProgressDelegate> delegate_;
588 virtual void Update() {
589 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
590 [delegate_ setProgressPercent:(Percent / 100)];
599 void setDelegate(id delegate) {
600 delegate_ = delegate;
603 virtual void Done() {
604 [delegate_ setProgressPercent:1];
609 /* Database Interface {{{ */
610 @interface Database : NSObject {
612 pkgDepCache::Policy *policy_;
613 pkgRecords *records_;
614 pkgProblemResolver *resolver_;
615 pkgAcquire *fetcher_;
617 SPtr<pkgPackageManager> manager_;
618 pkgSourceList *list_;
620 NSMutableDictionary *sources_;
621 NSMutableArray *packages_;
623 _transient id<ConfigurationDelegate, ProgressDelegate> delegate_;
632 - (void) _readCydia:(NSNumber *)fd;
633 - (void) _readStatus:(NSNumber *)fd;
634 - (void) _readOutput:(NSNumber *)fd;
638 - (Package *) packageWithName:(NSString *)name;
641 - (pkgCacheFile &) cache;
642 - (pkgDepCache::Policy *) policy;
643 - (pkgRecords *) records;
644 - (pkgProblemResolver *) resolver;
645 - (pkgAcquire &) fetcher;
646 - (NSArray *) packages;
655 - (void) updateWithStatus:(Status &)status;
657 - (void) setDelegate:(id)delegate;
658 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
662 /* Source Class {{{ */
663 @interface Source : NSObject {
664 NSString *description_;
669 NSString *distribution_;
673 NSString *defaultIcon_;
678 - (Source *) initWithMetaIndex:(metaIndex *)index;
683 - (NSString *) distribution;
686 - (NSString *) description;
687 - (NSString *) label;
688 - (NSString *) origin;
689 - (NSString *) version;
691 - (NSString *) defaultIcon;
694 @implementation Source
698 [distribution_ release];
701 if (description_ != nil)
702 [description_ release];
709 if (defaultIcon_ != nil)
710 [defaultIcon_ release];
715 - (Source *) initWithMetaIndex:(metaIndex *)index {
716 if ((self = [super init]) != nil) {
717 trusted_ = index->IsTrusted();
719 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
720 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
721 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
729 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
730 if (dindex != NULL) {
731 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
733 while (std::getline(release, line)) {
734 std::string::size_type colon(line.find(':'));
735 if (colon == std::string::npos)
738 std::string name(line.substr(0, colon));
739 std::string value(line.substr(colon + 1));
740 while (!value.empty() && value[0] == ' ')
741 value = value.substr(1);
743 if (name == "Default-Icon")
744 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
745 else if (name == "Description")
746 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
747 else if (name == "Label")
748 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
749 else if (name == "Origin")
750 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
751 else if (name == "Version")
752 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
766 - (NSString *) distribution {
767 return distribution_;
770 - (NSString *) type {
774 - (NSString *) description {
778 - (NSString *) label {
782 - (NSString *) origin {
786 - (NSString *) version {
790 - (NSString *) defaultIcon {
796 /* Relationship Class {{{ */
797 @interface Relationship : NSObject {
808 @implementation Relationship
816 - (NSString *) type {
824 - (NSString *) name {
831 /* Package Class {{{ */
832 NSString *Scour(const char *field, const char *begin, const char *end) {
833 size_t i(0), l(strlen(field));
836 const char *name = begin + i;
837 const char *colon = name + l;
838 const char *value = colon + 1;
843 memcmp(name, field, l) == 0
845 while (value != end && value[0] == ' ')
847 const char *line = std::find(value, end, '\n');
848 while (line != value && line[-1] == ' ')
851 return [NSString stringWithUTF8Bytes:value length:(line - value)];
853 begin = std::find(begin, end, '\n');
861 @interface Package : NSObject {
862 pkgCache::PkgIterator iterator_;
863 _transient Database *database_;
864 pkgCache::VerIterator version_;
865 pkgCache::VerFileIterator file_;
871 NSString *installed_;
880 NSArray *relationships_;
883 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
884 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
886 - (pkgCache::PkgIterator) iterator;
888 - (NSString *) section;
889 - (Address *) maintainer;
891 - (NSString *) description;
892 - (NSString *) index;
896 - (NSString *) latest;
897 - (NSString *) installed;
905 - (BOOL) halfConfigured;
906 - (BOOL) halfInstalled;
912 - (NSString *) tagline;
914 - (NSString *) website;
915 - (Address *) author;
917 - (NSArray *) relationships;
921 - (BOOL) matches:(NSString *)text;
923 - (NSComparisonResult) compareByName:(Package *)package;
924 - (NSComparisonResult) compareBySection:(Package *)package;
925 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
926 - (NSComparisonResult) compareForChanges:(Package *)package;
931 - (NSNumber *) isSearchedForBy:(NSString *)search;
932 - (NSNumber *) isInstalledInSection:(NSString *)section;
933 - (NSNumber *) isUninstalledInSection:(NSString *)section;
937 @implementation Package
944 if (installed_ != nil)
945 [installed_ release];
958 if (relationships_ != nil)
959 [relationships_ release];
964 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
965 if ((self = [super init]) != nil) {
966 iterator_ = iterator;
967 database_ = database;
969 version_ = [database_ policy]->GetCandidateVer(iterator_);
970 latest_ = version_.end() ? nil : [[NSString stringWithUTF8String:version_.VerStr()] retain];
973 file_ = version_.FileList();
975 pkgCache &cache([database_ cache]);
976 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
979 pkgCache::VerIterator current = iterator_.CurrentVer();
980 installed_ = current.end() ? nil : [[NSString stringWithUTF8String:current.VerStr()] retain];
982 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
985 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
987 const char *begin, *end;
988 parser->GetRec(begin, end);
990 name_ = Scour("Name", begin, end);
992 name_ = [name_ retain];
993 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
994 icon_ = Scour("Icon", begin, end);
996 icon_ = [icon_ retain];
997 website_ = Scour("Homepage", begin, end);
999 website_ = Scour("Website", begin, end);
1000 if (website_ != nil)
1001 website_ = [website_ retain];
1002 NSString *author = Scour("Author", begin, end);
1004 author_ = [Address addressWithString:author];
1007 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1008 if (metadata == nil || [metadata count] == 0) {
1009 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1013 [Packages_ setObject:metadata forKey:id_];
1019 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1020 return [[[Package alloc]
1021 initWithIterator:iterator
1026 - (pkgCache::PkgIterator) iterator {
1030 - (NSString *) section {
1031 const char *section = iterator_.Section();
1032 return section == NULL ? nil : [[NSString stringWithUTF8String:section] stringByReplacingCharacter:'_' withCharacter:' '];
1035 - (Address *) maintainer {
1038 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1039 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1043 return version_.end() ? 0 : version_->InstalledSize;
1046 - (NSString *) description {
1049 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1050 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1052 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1053 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1054 if ([lines count] < 2)
1057 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1058 for (size_t i(1); i != [lines count]; ++i) {
1059 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1060 [trimmed addObject:trim];
1063 return [trimmed componentsJoinedByString:@"\n"];
1066 - (NSString *) index {
1067 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1068 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1072 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1075 - (NSString *) latest {
1079 - (NSString *) installed {
1084 return !version_.end();
1087 - (BOOL) upgradable {
1088 pkgCache::VerIterator current = iterator_.CurrentVer();
1091 return [self essential];
1093 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1094 return !candidate.end() && candidate != current;
1098 - (BOOL) essential {
1099 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1103 return [database_ cache][iterator_].InstBroken();
1107 unsigned char current = iterator_->CurrentState;
1108 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1111 - (BOOL) halfConfigured {
1112 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1115 - (BOOL) halfInstalled {
1116 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1120 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1121 return state.Mode != pkgDepCache::ModeKeep;
1124 - (NSString *) mode {
1125 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1127 switch (state.Mode) {
1128 case pkgDepCache::ModeDelete:
1129 if ((state.iFlags & pkgDepCache::Purge) != 0)
1134 case pkgDepCache::ModeKeep:
1135 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1140 case pkgDepCache::ModeInstall:
1141 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1142 return @"Reinstall";
1143 else switch (state.Status) {
1145 return @"Downgrade";
1151 return @"New Install";
1164 - (NSString *) name {
1165 return name_ == nil ? id_ : name_;
1168 - (NSString *) tagline {
1172 - (NSString *) icon {
1176 - (NSString *) website {
1180 - (Address *) author {
1184 - (NSArray *) relationships {
1185 return relationships_;
1188 - (Source *) source {
1190 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1197 - (BOOL) matches:(NSString *)text {
1203 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1204 if (range.location != NSNotFound)
1207 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1208 if (range.location != NSNotFound)
1211 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1212 if (range.location != NSNotFound)
1218 - (NSComparisonResult) compareByName:(Package *)package {
1219 NSString *lhs = [self name];
1220 NSString *rhs = [package name];
1222 if ([lhs length] != 0 && [rhs length] != 0) {
1223 unichar lhc = [lhs characterAtIndex:0];
1224 unichar rhc = [rhs characterAtIndex:0];
1226 if (isalpha(lhc) && !isalpha(rhc))
1227 return NSOrderedAscending;
1228 else if (!isalpha(lhc) && isalpha(rhc))
1229 return NSOrderedDescending;
1232 return [lhs caseInsensitiveCompare:rhs];
1235 - (NSComparisonResult) compareBySection:(Package *)package {
1236 NSString *lhs = [self section];
1237 NSString *rhs = [package section];
1239 if (lhs == NULL && rhs != NULL)
1240 return NSOrderedAscending;
1241 else if (lhs != NULL && rhs == NULL)
1242 return NSOrderedDescending;
1243 else if (lhs != NULL && rhs != NULL) {
1244 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1245 if (result != NSOrderedSame)
1249 return NSOrderedSame;
1252 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1253 NSString *lhs = [self section];
1254 NSString *rhs = [package section];
1256 if (lhs == NULL && rhs != NULL)
1257 return NSOrderedAscending;
1258 else if (lhs != NULL && rhs == NULL)
1259 return NSOrderedDescending;
1260 else if (lhs != NULL && rhs != NULL) {
1261 NSComparisonResult result = [lhs compare:rhs];
1262 if (result != NSOrderedSame)
1266 return [self compareByName:package];
1269 - (NSComparisonResult) compareForChanges:(Package *)package {
1270 BOOL lhs = [self upgradable];
1271 BOOL rhs = [package upgradable];
1274 return lhs ? NSOrderedAscending : NSOrderedDescending;
1276 switch ([[self seen] compare:[package seen]]) {
1277 case NSOrderedAscending:
1278 return NSOrderedDescending;
1281 case NSOrderedDescending:
1282 return NSOrderedAscending;
1288 return [self compareByName:package];
1292 pkgProblemResolver *resolver = [database_ resolver];
1293 resolver->Clear(iterator_);
1294 resolver->Protect(iterator_);
1295 pkgCacheFile &cache([database_ cache]);
1296 cache->MarkInstall(iterator_, false);
1297 pkgDepCache::StateCache &state((*cache)[iterator_]);
1298 if (!state.Install())
1299 cache->SetReInstall(iterator_, true);
1303 pkgProblemResolver *resolver = [database_ resolver];
1304 resolver->Clear(iterator_);
1305 resolver->Protect(iterator_);
1306 resolver->Remove(iterator_);
1307 [database_ cache]->MarkDelete(iterator_, true);
1310 - (NSNumber *) isSearchedForBy:(NSString *)search {
1311 return [NSNumber numberWithBool:([self valid] && [self matches:search])];
1314 - (NSNumber *) isInstalledInSection:(NSString *)section {
1315 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1318 - (NSNumber *) isUninstalledInSection:(NSString *)name {
1319 NSString *section = [self section];
1321 return [NSNumber numberWithBool:([self valid] && [self installed] == nil && (
1323 section == nil && [name length] == 0 ||
1324 [name isEqualToString:section])
1330 /* Section Class {{{ */
1331 @interface Section : NSObject {
1337 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1338 - (NSString *) name;
1341 - (void) addToCount;
1345 @implementation Section
1352 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1353 if ((self = [super init]) != nil) {
1354 name_ = [name retain];
1359 - (NSString *) name {
1371 - (void) addToCount {
1378 /* Database Implementation {{{ */
1379 @implementation Database
1386 - (void) _readCydia:(NSNumber *)fd {
1387 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1389 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1390 std::istream is(&ib);
1393 while (std::getline(is, line)) {
1394 const char *data(line.c_str());
1395 //size_t size = line.size();
1396 fprintf(stderr, "C:%s\n", data);
1403 - (void) _readStatus:(NSNumber *)fd {
1404 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1406 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1407 std::istream is(&ib);
1410 Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1411 Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1413 while (std::getline(is, line)) {
1414 const char *data(line.c_str());
1415 size_t size = line.size();
1416 fprintf(stderr, "S:%s\n", data);
1418 if (conffile_r(data, size)) {
1419 [delegate_ setConfigurationData:conffile_r[1]];
1420 } else if (strncmp(data, "status: ", 8) == 0) {
1421 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1422 [delegate_ setProgressTitle:string];
1423 } else if (pmstatus_r(data, size)) {
1424 float percent([pmstatus_r[3] floatValue]);
1425 [delegate_ setProgressPercent:(percent / 100)];
1427 NSString *string = pmstatus_r[4];
1428 std::string type([pmstatus_r[1] UTF8String]);
1430 if (type == "pmerror")
1431 [delegate_ setProgressError:string];
1432 else if (type == "pmstatus")
1433 [delegate_ setProgressTitle:string];
1434 else if (type == "pmconffile")
1435 [delegate_ setConfigurationData:string];
1436 else _assert(false);
1437 } else _assert(false);
1444 - (void) _readOutput:(NSNumber *)fd {
1445 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1447 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1448 std::istream is(&ib);
1451 while (std::getline(is, line)) {
1452 fprintf(stderr, "O:%s\n", line.c_str());
1453 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1464 - (Package *) packageWithName:(NSString *)name {
1465 if (static_cast<pkgDepCache *>(cache_) == NULL)
1467 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1468 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1471 - (Database *) init {
1472 if ((self = [super init]) != nil) {
1479 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1480 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1484 _assert(pipe(fds) != -1);
1487 _config->Set("APT::Keep-Fds::", cydiafd_);
1488 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 0"] UTF8String], _not(int));
1491 detachNewThreadSelector:@selector(_readCydia:)
1493 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1496 _assert(pipe(fds) != -1);
1500 detachNewThreadSelector:@selector(_readStatus:)
1502 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1505 _assert(pipe(fds) != -1);
1506 _assert(dup2(fds[0], 0) != -1);
1507 _assert(close(fds[0]) != -1);
1509 input_ = fdopen(fds[1], "a");
1511 _assert(pipe(fds) != -1);
1512 _assert(dup2(fds[1], 1) != -1);
1513 _assert(close(fds[1]) != -1);
1516 detachNewThreadSelector:@selector(_readOutput:)
1518 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1523 - (pkgCacheFile &) cache {
1527 - (pkgDepCache::Policy *) policy {
1531 - (pkgRecords *) records {
1535 - (pkgProblemResolver *) resolver {
1539 - (pkgAcquire &) fetcher {
1543 - (NSArray *) packages {
1547 - (void) reloadData {
1566 if (!cache_.Open(progress_, true)) {
1568 if (!_error->PopMessage(error))
1571 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1573 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1574 [delegate_ repairWithSelector:@selector(configure)];
1575 else if (error == "The package lists or status file could not be parsed or opened.")
1576 [delegate_ repairWithSelector:@selector(update)];
1577 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1578 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1579 // else if (error == "The list of sources could not be read.")
1580 else _assert(false);
1585 now_ = [[NSDate date] retain];
1587 policy_ = new pkgDepCache::Policy();
1588 records_ = new pkgRecords(cache_);
1589 resolver_ = new pkgProblemResolver(cache_);
1590 fetcher_ = new pkgAcquire(&status_);
1593 list_ = new pkgSourceList();
1594 _assert(list_->ReadMainList());
1596 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1597 _assert(pkgApplyStatus(cache_));
1599 if (cache_->BrokenCount() != 0) {
1600 _assert(pkgFixBroken(cache_));
1601 _assert(cache_->BrokenCount() == 0);
1602 _assert(pkgMinimizeUpgrade(cache_));
1605 [sources_ removeAllObjects];
1606 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1607 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1608 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1610 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1611 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1615 [packages_ removeAllObjects];
1616 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1617 if (Package *package = [Package packageWithIterator:iterator database:self])
1618 [packages_ addObject:package];
1620 [packages_ sortUsingSelector:@selector(compareByName:)];
1623 - (void) configure {
1624 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1625 system([dpkg UTF8String]);
1633 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1634 _assert(!_error->PendingError());
1637 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1640 public pkgArchiveCleaner
1643 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1648 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1650 while (_error->PopMessage(error))
1651 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1656 pkgRecords records(cache_);
1658 lock_ = new FileFd();
1659 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1660 _assert(!_error->PendingError());
1663 // XXX: explain this with an error message
1664 _assert(list.ReadMainList());
1666 manager_ = (_system->CreatePM(cache_));
1667 _assert(manager_->GetArchives(fetcher_, &list, &records));
1668 _assert(!_error->PendingError());
1672 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1674 _assert(list.ReadMainList());
1675 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1676 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1679 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1683 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1685 if (result == pkgPackageManager::Failed)
1687 if (_error->PendingError())
1689 if (result != pkgPackageManager::Completed)
1692 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1694 _assert(list.ReadMainList());
1695 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1696 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1699 if (![before isEqualToArray:after])
1704 _assert(pkgDistUpgrade(cache_));
1708 [self updateWithStatus:status_];
1711 - (void) updateWithStatus:(Status &)status {
1713 _assert(list.ReadMainList());
1716 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1717 _assert(!_error->PendingError());
1719 pkgAcquire fetcher(&status);
1720 _assert(list.GetIndexes(&fetcher));
1722 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1723 bool failed = false;
1724 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1725 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1726 (*item)->Finished();
1730 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1731 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1732 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1735 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1740 - (void) setDelegate:(id)delegate {
1741 delegate_ = delegate;
1742 status_.setDelegate(delegate);
1743 progress_.setDelegate(delegate);
1746 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1747 pkgIndexFile *index(NULL);
1748 list_->FindIndex(file, index);
1749 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1755 /* Confirmation View {{{ */
1756 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1757 if ([packages count] == 0)
1760 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1761 [fields setObject:text forKey:key];
1763 CGColor blue(space_, 0, 0, 0.4, 1);
1764 [text setTextColor:blue];
1767 @protocol ConfirmationViewDelegate
1772 @interface ConfirmationView : UIView {
1773 Database *database_;
1775 UITransitionView *transition_;
1777 UINavigationBar *navbar_;
1778 UIPreferencesTable *table_;
1779 NSMutableDictionary *fields_;
1780 UIAlertSheet *essential_;
1785 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1789 @implementation ConfirmationView
1792 [navbar_ setDelegate:nil];
1793 [transition_ setDelegate:nil];
1794 [table_ setDataSource:nil];
1796 [transition_ release];
1801 if (essential_ != nil)
1802 [essential_ release];
1807 [transition_ transition:7 toView:nil];
1811 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1812 if (from != nil && to == nil)
1813 [self removeFromSuperview];
1816 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1819 if (essential_ != nil)
1820 [essential_ popupAlertAnimated:YES];
1822 [delegate_ confirm];
1831 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1832 NSString *context = [sheet context];
1834 if ([context isEqualToString:@"remove"])
1837 [delegate_ confirm];
1845 else if ([context isEqualToString:@"unable"])
1851 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1855 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1857 case 0: return @"Statistics";
1858 case 1: return @"Modifications";
1860 default: _assert(false);
1864 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1867 case 1: return [fields_ count];
1869 default: _assert(false);
1873 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1874 if (group != 1 || row == -1)
1877 _assert(size_t(row) < [fields_ count]);
1878 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1882 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1883 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1884 [cell setShowSelection:NO];
1887 case 0: switch (row) {
1889 [cell setTitle:@"Downloading"];
1890 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1894 [cell setTitle:@"Resuming At"];
1895 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1899 double size([database_ cache]->UsrSize());
1902 [cell setTitle:@"Disk Freeing"];
1903 [cell setValue:SizeString(-size)];
1905 [cell setTitle:@"Disk Using"];
1906 [cell setValue:SizeString(size)];
1910 default: _assert(false);
1914 _assert(size_t(row) < [fields_ count]);
1915 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1916 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1919 default: _assert(false);
1925 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1926 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1927 database_ = database;
1928 delegate_ = delegate;
1930 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1931 [self addSubview:transition_];
1933 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1935 CGSize navsize = [UINavigationBar defaultSize];
1936 CGRect navrect = {{0, 0}, navsize};
1937 CGRect bounds = [overlay_ bounds];
1939 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1941 [navbar_ setBarStyle:1];
1942 [navbar_ setDelegate:self];
1944 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1945 [navbar_ pushNavigationItem:navitem];
1946 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
1948 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1950 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1951 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1952 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1953 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1954 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
1958 pkgCacheFile &cache([database_ cache]);
1959 NSArray *packages = [database_ packages];
1960 for (size_t i(0), e = [packages count]; i != e; ++i) {
1961 Package *package = [packages objectAtIndex:i];
1962 pkgCache::PkgIterator iterator = [package iterator];
1963 pkgDepCache::StateCache &state(cache[iterator]);
1965 NSString *name([package name]);
1967 if (state.NewInstall())
1968 [installing addObject:name];
1969 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
1970 [reinstalling addObject:name];
1971 else if (state.Upgrade())
1972 [upgrading addObject:name];
1973 else if (state.Downgrade())
1974 [downgrading addObject:name];
1975 else if (state.Delete()) {
1976 if ([package essential])
1978 [removing addObject:name];
1984 else if (Advanced_ || true) {
1985 essential_ = [[UIAlertSheet alloc]
1986 initWithTitle:@"Remove Essential?"
1987 buttons:[NSArray arrayWithObjects:
1988 @"Yes (Force Removal)",
1989 @"No (Safe, Recommended)",
1991 defaultButtonIndex:1
1996 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
1997 [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or the iPhoneOS. If you continue you will almost certainly break something past Cydia's ability to fix it. Are you absolutely certain you wish to continue?"];
1999 essential_ = [[UIAlertSheet alloc]
2000 initWithTitle:@"Unable to Comply"
2001 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2002 defaultButtonIndex:0
2007 [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or the iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode undder to continue and force this operation you will need to be activate the Advanced mode under Settings."];
2010 AddTextView(fields_, installing, @"Installing");
2011 AddTextView(fields_, reinstalling, @"Reinstalling");
2012 AddTextView(fields_, upgrading, @"Upgrading");
2013 AddTextView(fields_, downgrading, @"Downgrading");
2014 AddTextView(fields_, removing, @"Removing");
2016 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2017 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2020 [table_ setReusesTableCells:YES];
2021 [table_ setDataSource:self];
2022 [table_ reloadData];
2024 [overlay_ addSubview:navbar_];
2025 [overlay_ addSubview:table_];
2027 [view addSubview:self];
2029 [transition_ setDelegate:self];
2031 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2032 [transition_ transition:0 toView:blank];
2033 [transition_ transition:3 toView:overlay_];
2040 /* Progress Data {{{ */
2041 @interface ProgressData : NSObject {
2047 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2054 @implementation ProgressData
2056 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2057 if ((self = [super init]) != nil) {
2058 selector_ = selector;
2078 /* Progress View {{{ */
2079 Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2081 @interface ProgressView : UIView <
2082 ConfigurationDelegate,
2085 _transient Database *database_;
2087 UIView *background_;
2088 UITransitionView *transition_;
2090 UINavigationBar *navbar_;
2091 UIProgressBar *progress_;
2092 UITextView *output_;
2093 UITextLabel *status_;
2097 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2099 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2100 - (void) setContentView:(UIView *)view;
2103 - (void) _retachThread;
2104 - (void) _detachNewThreadData:(ProgressData *)data;
2105 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2109 @protocol ProgressViewDelegate
2110 - (void) progressViewIsComplete:(ProgressView *)sender;
2113 @implementation ProgressView
2116 [transition_ setDelegate:nil];
2117 [navbar_ setDelegate:nil];
2120 if (background_ != nil)
2121 [background_ release];
2122 [transition_ release];
2125 [progress_ release];
2131 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2132 if (bootstrap_ && from == overlay_ && to == view_)
2136 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2137 if ((self = [super initWithFrame:frame]) != nil) {
2138 database_ = database;
2139 delegate_ = delegate;
2141 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2142 [transition_ setDelegate:self];
2144 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2147 [overlay_ setBackgroundColor:Black_];
2149 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2150 [background_ setBackgroundColor:Black_];
2151 [self addSubview:background_];
2154 [self addSubview:transition_];
2156 CGSize navsize = [UINavigationBar defaultSize];
2157 CGRect navrect = {{0, 0}, navsize};
2159 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2160 [overlay_ addSubview:navbar_];
2162 [navbar_ setBarStyle:1];
2163 [navbar_ setDelegate:self];
2165 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2166 [navbar_ pushNavigationItem:navitem];
2168 CGRect bounds = [overlay_ bounds];
2169 CGSize prgsize = [UIProgressBar defaultSize];
2172 (bounds.size.width - prgsize.width) / 2,
2173 bounds.size.height - prgsize.height - 20
2176 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2177 [overlay_ addSubview:progress_];
2179 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2181 bounds.size.height - prgsize.height - 50,
2182 bounds.size.width - 20,
2186 [status_ setColor:White_];
2187 [status_ setBackgroundColor:Clear_];
2189 [status_ setCentersHorizontally:YES];
2190 //[status_ setFont:font];
2192 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2194 navrect.size.height + 20,
2195 bounds.size.width - 20,
2196 bounds.size.height - navsize.height - 62 - navrect.size.height
2199 //[output_ setTextFont:@"Courier New"];
2200 [output_ setTextSize:12];
2202 [output_ setTextColor:White_];
2203 [output_ setBackgroundColor:Clear_];
2205 [output_ setMarginTop:0];
2206 [output_ setAllowsRubberBanding:YES];
2208 [overlay_ addSubview:output_];
2209 [overlay_ addSubview:status_];
2211 [progress_ setStyle:0];
2215 - (void) setContentView:(UIView *)view {
2216 view_ = [view retain];
2219 - (void) resetView {
2220 [transition_ transition:6 toView:view_];
2223 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2224 NSString *context = [sheet context];
2225 if ([context isEqualToString:@"conffile"]) {
2226 FILE *input = [database_ input];
2230 fprintf(input, "N\n");
2234 fprintf(input, "Y\n");
2245 - (void) _retachThread {
2246 [delegate_ progressViewIsComplete:self];
2250 - (void) _detachNewThreadData:(ProgressData *)data {
2251 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2253 [[data target] performSelector:[data selector] withObject:[data object]];
2256 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2261 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2262 UINavigationItem *item = [navbar_ topItem];
2263 [item setTitle:title];
2265 [status_ setText:nil];
2266 [output_ setText:@""];
2267 [progress_ setProgress:0];
2269 [transition_ transition:6 toView:overlay_];
2272 detachNewThreadSelector:@selector(_detachNewThreadData:)
2274 withObject:[[ProgressData alloc]
2275 initWithSelector:selector
2282 - (void) repairWithSelector:(SEL)selector {
2284 detachNewThreadSelector:selector
2287 title:@"Repairing..."
2291 - (void) setConfigurationData:(NSString *)data {
2293 performSelectorOnMainThread:@selector(_setConfigurationData:)
2299 - (void) setProgressError:(NSString *)error {
2301 performSelectorOnMainThread:@selector(_setProgressError:)
2307 - (void) setProgressTitle:(NSString *)title {
2309 performSelectorOnMainThread:@selector(_setProgressTitle:)
2315 - (void) setProgressPercent:(float)percent {
2317 performSelectorOnMainThread:@selector(_setProgressPercent:)
2318 withObject:[NSNumber numberWithFloat:percent]
2323 - (void) addProgressOutput:(NSString *)output {
2325 performSelectorOnMainThread:@selector(_addProgressOutput:)
2331 - (void) _setConfigurationData:(NSString *)data {
2332 _assert(conffile_r(data));
2334 NSString *ofile = conffile_r[1];
2335 //NSString *nfile = conffile_r[2];
2337 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2338 initWithTitle:@"Configuration Upgrade"
2339 buttons:[NSArray arrayWithObjects:
2340 @"Keep My Old Copy",
2341 @"Accept The New Copy",
2342 // XXX: @"See What Changed",
2344 defaultButtonIndex:0
2349 [sheet setBodyText:[NSString stringWithFormat:
2350 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2353 [sheet popupAlertAnimated:YES];
2356 - (void) _setProgressError:(NSString *)error {
2357 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2358 initWithTitle:@"Package Error"
2359 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2360 defaultButtonIndex:0
2365 [sheet setBodyText:error];
2366 [sheet popupAlertAnimated:YES];
2369 - (void) _setProgressTitle:(NSString *)title {
2370 [status_ setText:[title stringByAppendingString:@"..."]];
2373 - (void) _setProgressPercent:(NSNumber *)percent {
2374 [progress_ setProgress:[percent floatValue]];
2377 - (void) _addProgressOutput:(NSString *)output {
2378 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2379 CGSize size = [output_ contentSize];
2380 CGRect rect = {{0, size.height}, {size.width, 0}};
2381 [output_ scrollRectToVisible:rect animated:YES];
2387 /* Package Cell {{{ */
2388 @interface PackageCell : UITableCell {
2391 UITextLabel *description_;
2392 UITextLabel *source_;
2393 //UIImageView *trusted_;
2394 UIImageView *badge_;
2395 UITextLabel *status_;
2398 - (PackageCell *) init;
2399 - (void) setPackage:(Package *)package;
2401 - (void) _setSelected:(float)fraction;
2402 - (void) setSelected:(BOOL)selected;
2403 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2404 - (void) _setSelectionFadeFraction:(float)fraction;
2406 + (int) heightForPackage:(Package *)package;
2410 @implementation PackageCell
2415 [description_ release];
2419 //[trusted_ release];
2423 - (PackageCell *) init {
2424 if ((self = [super init]) != nil) {
2425 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2426 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2427 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2429 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2431 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2432 [name_ setBackgroundColor:Clear_];
2433 [name_ setFont:bold];
2435 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2436 [source_ setBackgroundColor:Clear_];
2437 [source_ setFont:large];
2439 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2440 [description_ setBackgroundColor:Clear_];
2441 [description_ setFont:small];
2443 /*trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2444 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];*/
2446 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2448 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2449 [status_ setBackgroundColor:Clear_];
2450 [status_ setFont:small];
2452 [self addSubview:icon_];
2453 [self addSubview:name_];
2454 [self addSubview:description_];
2455 [self addSubview:source_];
2456 [self addSubview:badge_];
2457 [self addSubview:status_];
2465 - (void) setPackage:(Package *)package {
2466 Source *source = [package source];
2468 UIImage *image = nil;
2469 if (NSString *icon = [package icon])
2470 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2471 if (image == nil) if (NSString *icon = [source defaultIcon])
2472 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2474 image = [UIImage applicationImageNamed:@"unknown.png"];
2475 [icon_ setImage:image];
2478 CGSize size = [image size];
2479 float scale = 30 / std::max(size.width, size.height);
2480 [icon_ zoomToScale:scale];
2483 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2485 [name_ setText:[package name]];
2486 [description_ setText:[package tagline]];
2488 NSString *label = nil;
2489 bool trusted = false;
2491 if (source != nil) {
2492 label = [source label];
2493 trusted = [source trusted];
2494 } else if ([[package id] isEqualToString:@"firmware"])
2498 label = @"Unknown/Local";
2500 NSString *from = [NSString stringWithFormat:@"from %@", label];
2502 NSString *section = Simplify([package section]);
2503 if (section != nil && ![section isEqualToString:label])
2504 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2506 [source_ setText:from];
2508 if (NSString *mode = [package mode]) {
2509 [badge_ setImage:[UIImage applicationImageNamed:
2510 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2513 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2514 [status_ setColor:Blueish_];
2515 } else if ([package half]) {
2516 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2517 [status_ setText:@"Package Damaged"];
2518 [status_ setColor:Red_];
2520 [badge_ setImage:nil];
2521 [status_ setText:nil];
2525 - (void) _setSelected:(float)fraction {
2526 CGColor black(space_,
2527 Interpolate(0.0, 1.0, fraction),
2528 Interpolate(0.0, 1.0, fraction),
2529 Interpolate(0.0, 1.0, fraction),
2532 CGColor gray(space_,
2533 Interpolate(0.4, 1.0, fraction),
2534 Interpolate(0.4, 1.0, fraction),
2535 Interpolate(0.4, 1.0, fraction),
2538 [name_ setColor:black];
2539 [description_ setColor:gray];
2540 [source_ setColor:black];
2543 - (void) setSelected:(BOOL)selected {
2544 [self _setSelected:(selected ? 1.0 : 0.0)];
2545 [super setSelected:selected];
2548 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2550 [self _setSelected:(selected ? 1.0 : 0.0)];
2551 [super setSelected:selected withFade:fade];
2554 - (void) _setSelectionFadeFraction:(float)fraction {
2555 [self _setSelected:fraction];
2556 [super _setSelectionFadeFraction:fraction];
2559 + (int) heightForPackage:(Package *)package {
2560 if ([package hasMode] || [package half])
2568 /* Section Cell {{{ */
2569 @interface SectionCell : UITableCell {
2571 UITextLabel *count_;
2575 - (void) setSection:(Section *)section;
2577 - (void) _setSelected:(float)fraction;
2578 - (void) setSelected:(BOOL)selected;
2579 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2580 - (void) _setSelectionFadeFraction:(float)fraction;
2584 @implementation SectionCell
2593 if ((self = [super init]) != nil) {
2594 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2595 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2597 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2598 [name_ setBackgroundColor:Clear_];
2599 [name_ setFont:bold];
2601 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2602 [count_ setCentersHorizontally:YES];
2603 [count_ setBackgroundColor:Clear_];
2604 [count_ setFont:small];
2605 [count_ setColor:White_];
2607 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2608 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2610 [self addSubview:folder];
2611 [self addSubview:name_];
2612 [self addSubview:count_];
2614 [self _setSelected:0];
2621 - (void) setSection:(Section *)section {
2622 if (section == nil) {
2623 [name_ setText:@"All Packages"];
2624 [count_ setText:nil];
2626 NSString *name = [section name];
2627 [name_ setText:(name == nil ? @"(No Section)" : name)];
2628 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2632 - (void) _setSelected:(float)fraction {
2633 CGColor black(space_,
2634 Interpolate(0.0, 1.0, fraction),
2635 Interpolate(0.0, 1.0, fraction),
2636 Interpolate(0.0, 1.0, fraction),
2639 [name_ setColor:black];
2642 - (void) setSelected:(BOOL)selected {
2643 [self _setSelected:(selected ? 1.0 : 0.0)];
2644 [super setSelected:selected];
2647 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2649 [self _setSelected:(selected ? 1.0 : 0.0)];
2650 [super setSelected:selected withFade:fade];
2653 - (void) _setSelectionFadeFraction:(float)fraction {
2654 [self _setSelected:fraction];
2655 [super _setSelectionFadeFraction:fraction];
2661 /* File Table {{{ */
2662 @interface FileTable : RVPage {
2663 _transient Database *database_;
2666 NSMutableArray *files_;
2670 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2671 - (void) setPackage:(Package *)package;
2675 @implementation FileTable
2678 if (package_ != nil)
2687 - (int) numberOfRowsInTable:(UITable *)table {
2688 return files_ == nil ? 0 : [files_ count];
2691 - (float) table:(UITable *)table heightForRow:(int)row {
2695 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2696 if (reusing == nil) {
2697 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
2698 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2699 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
2702 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
2706 - (BOOL) canSelectRow:(int)row {
2710 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2711 if ((self = [super initWithBook:book]) != nil) {
2712 database_ = database;
2714 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
2716 list_ = [[UITable alloc] initWithFrame:[self bounds]];
2717 [self addSubview:list_];
2719 UITableColumn *column = [[[UITableColumn alloc]
2720 initWithTitle:@"Name"
2722 width:[self frame].size.width
2725 [list_ setDataSource:self];
2726 [list_ setSeparatorStyle:1];
2727 [list_ addTableColumn:column];
2728 [list_ setDelegate:self];
2729 [list_ setReusesTableCells:YES];
2733 - (void) setPackage:(Package *)package {
2734 if (package_ != nil) {
2735 [package_ autorelease];
2744 [files_ removeAllObjects];
2746 if (package != nil) {
2747 package_ = [package retain];
2748 name_ = [[package id] retain];
2750 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
2753 std::ifstream fin([path UTF8String]);
2755 while (std::getline(fin, line))
2756 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
2759 if ([files_ count] != 0) {
2760 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
2761 [files_ removeObjectAtIndex:0];
2762 [files_ sortUsingSelector:@selector(compareByPath:)];
2764 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
2765 [stack addObject:@"/"];
2767 for (int i(0), e([files_ count]); i != e; ++i) {
2768 NSString *file = [files_ objectAtIndex:i];
2769 while (![file hasPrefix:[stack lastObject]])
2770 [stack removeLastObject];
2771 NSString *directory = [stack lastObject];
2772 [stack addObject:[file stringByAppendingString:@"/"]];
2773 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
2774 ([stack count] - 2) * 3, "",
2775 [file substringFromIndex:[directory length]]
2784 - (void) resetViewAnimated:(BOOL)animated {
2785 [list_ resetViewAnimated:animated];
2788 - (void) reloadData {
2789 [self setPackage:[database_ packageWithName:name_]];
2790 [self reloadButtons];
2793 - (NSString *) title {
2794 return @"Installed Files";
2797 - (NSString *) backButtonTitle {
2803 /* Package View {{{ */
2804 @protocol PackageViewDelegate
2805 - (void) performPackage:(Package *)package;
2808 @interface PackageView : RVPage {
2809 _transient Database *database_;
2810 UIPreferencesTable *table_;
2813 UITextView *description_;
2814 NSMutableArray *buttons_;
2817 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2818 - (void) setPackage:(Package *)package;
2822 @implementation PackageView
2825 [table_ setDataSource:nil];
2826 [table_ setDelegate:nil];
2828 if (package_ != nil)
2832 if (description_ != nil)
2833 [description_ release];
2839 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2841 if ([package_ installed] != nil)
2843 if ([package_ source] != nil)
2848 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2851 else if ([package_ installed] != nil && group-- == 0)
2852 return @"Installed Package";
2853 else if (group-- == 0)
2854 return @"Package Details";
2855 else if ([package_ source] != nil && group-- == 0)
2856 return @"Source Information";
2857 else _assert(false);
2860 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2861 if (description_ == nil || group != 0 || row != 1)
2864 return [description_ visibleTextRect].size.height + TextViewOffset_;
2867 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2870 if ([package_ author] != nil)
2872 if (description_ != nil)
2874 if ([package_ website] != nil)
2876 if ([[package_ source] trusted])
2879 } else if ([package_ installed] != nil && group-- == 0)
2881 else if (group-- == 0) {
2883 if ([package_ size] != 0)
2885 if ([package_ maintainer] != nil)
2887 if ([package_ relationships] != nil)
2890 } else if ([package_ source] != nil && group-- == 0) {
2891 Source *source = [package_ source];
2892 NSString *description = [source description];
2894 if (description != nil && ![description isEqualToString:[source label]])
2896 if ([source origin] != nil)
2899 } else _assert(false);
2902 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2903 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2904 [cell setShowSelection:NO];
2908 [cell setTitle:[package_ name]];
2909 [cell setValue:[package_ latest]];
2910 } else if ([package_ author] != nil && row-- == 0) {
2911 [cell setTitle:@"Author"];
2912 [cell setValue:[[package_ author] name]];
2913 [cell setShowDisclosure:YES];
2914 [cell setShowSelection:YES];
2915 } else if (description_ != nil && row-- == 0) {
2916 [cell addSubview:description_];
2917 } else if ([package_ website] != nil && row-- == 0) {
2918 [cell setTitle:@"More Information"];
2919 [cell setShowDisclosure:YES];
2920 [cell setShowSelection:YES];
2921 } else if ([[package_ source] trusted] && row-- == 0) {
2922 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
2923 [cell setValue:@"This package has been signed."];
2924 } else _assert(false);
2925 } else if ([package_ installed] != nil && group-- == 0) {
2927 [cell setTitle:@"Version"];
2928 NSString *installed([package_ installed]);
2929 [cell setValue:(installed == nil ? @"n/a" : installed)];
2930 } else if (row-- == 0) {
2931 [cell setTitle:@"Filesystem Content"];
2932 [cell setShowDisclosure:YES];
2933 [cell setShowSelection:YES];
2934 } else _assert(false);
2935 } else if (group-- == 0) {
2937 [cell setTitle:@"Identifier"];
2938 [cell setValue:[package_ id]];
2939 } else if (row-- == 0) {
2940 [cell setTitle:@"Section"];
2941 NSString *section([package_ section]);
2942 [cell setValue:(section == nil ? @"n/a" : section)];
2943 } else if ([package_ size] != 0 && row-- == 0) {
2944 [cell setTitle:@"Expanded Size"];
2945 [cell setValue:SizeString([package_ size])];
2946 } else if ([package_ maintainer] != nil && row-- == 0) {
2947 [cell setTitle:@"Maintainer"];
2948 [cell setValue:[[package_ maintainer] name]];
2949 [cell setShowDisclosure:YES];
2950 [cell setShowSelection:YES];
2951 } else if ([package_ relationships] != nil && row-- == 0) {
2952 [cell setTitle:@"Package Relationships"];
2953 [cell setShowDisclosure:YES];
2954 [cell setShowSelection:YES];
2955 } else _assert(false);
2956 } else if ([package_ source] != nil && group-- == 0) {
2957 Source *source = [package_ source];
2958 NSString *description = [source description];
2961 NSString *label = [source label];
2963 label = [source uri];
2964 [cell setTitle:label];
2965 [cell setValue:[source version]];
2966 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
2967 [cell setValue:description];
2968 } else if ([source origin] != nil && row-- == 0) {
2969 [cell setTitle:@"Origin"];
2970 [cell setValue:[source origin]];
2971 } else _assert(false);
2972 } else _assert(false);
2977 - (BOOL) canSelectRow:(int)row {
2981 // XXX: this is now unmaintainable
2982 - (void) tableRowSelected:(NSNotification *)notification {
2983 int row = [table_ selectedRow];
2984 NSString *website = [package_ website];
2985 Address *author = [package_ author];
2986 BOOL trusted = [[package_ source] trusted];
2987 NSString *installed = [package_ installed];
2988 Address *maintainer = [package_ maintainer];
2990 if (maintainer != nil && row == 7
2991 + (author == nil ? 0 : 1)
2992 + (website == nil ? 0 : 1)
2994 + (installed == nil ? 0 : 3)
2996 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2998 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
3000 } else if (installed && row == 5
3001 + (author == nil ? 0 : 1)
3002 + (website == nil ? 0 : 1)
3005 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3006 [files setDelegate:delegate_];
3007 [files setPackage:package_];
3008 [book_ pushPage:files];
3009 } else if (author != nil && row == 2) {
3010 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3012 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
3014 } else if (website != nil && row == (author == nil ? 3 : 4)) {
3015 NSURL *url = [NSURL URLWithString:website];
3016 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3017 [browser setDelegate:delegate_];
3018 [book_ pushPage:browser];
3019 [browser loadURL:url];
3023 - (void) _clickButtonWithName:(NSString *)name {
3024 if ([name isEqualToString:@"Install"])
3025 [delegate_ installPackage:package_];
3026 else if ([name isEqualToString:@"Reinstall"])
3027 [delegate_ installPackage:package_];
3028 else if ([name isEqualToString:@"Remove"])
3029 [delegate_ removePackage:package_];
3030 else if ([name isEqualToString:@"Upgrade"])
3031 [delegate_ installPackage:package_];
3032 else _assert(false);
3035 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3036 int count = [buttons_ count];
3037 _assert(count != 0);
3038 _assert(button <= count + 1);
3040 if (count != button - 1)
3041 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3046 - (void) _rightButtonClicked {
3047 int count = [buttons_ count];
3048 _assert(count != 0);
3051 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3053 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3054 [buttons addObjectsFromArray:buttons_];
3055 [buttons addObject:@"Cancel"];
3057 [delegate_ slideUp:[[[UIAlertSheet alloc]
3060 defaultButtonIndex:2
3067 - (NSString *) rightButtonTitle {
3068 int count = [buttons_ count];
3069 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3072 - (NSString *) title {
3076 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3077 if ((self = [super initWithBook:book]) != nil) {
3078 database_ = database;
3080 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3081 [self addSubview:table_];
3083 [table_ setDataSource:self];
3084 [table_ setDelegate:self];
3086 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3090 - (void) setPackage:(Package *)package {
3091 if (package_ != nil) {
3092 [package_ autorelease];
3101 if (description_ != nil) {
3102 [description_ release];
3106 [buttons_ removeAllObjects];
3108 if (package != nil) {
3109 package_ = [package retain];
3110 name_ = [[package id] retain];
3112 NSString *description([package description]);
3113 if (description == nil)
3114 description = [package tagline];
3115 if (description != nil) {
3116 description_ = [GetTextView(description, 12, true) retain];
3117 [description_ setTextColor:Black_];
3120 [table_ reloadData];
3122 if ([package_ source] == nil);
3123 else if ([package_ installed] == nil)
3124 [buttons_ addObject:@"Install"];
3125 else if ([package_ upgradable])
3126 [buttons_ addObject:@"Upgrade"];
3128 [buttons_ addObject:@"Reinstall"];
3129 if ([package_ installed] != nil)
3130 [buttons_ addObject:@"Remove"];
3134 - (void) resetViewAnimated:(BOOL)animated {
3135 [table_ resetViewAnimated:animated];
3138 - (void) reloadData {
3139 [self setPackage:[database_ packageWithName:name_]];
3140 [self reloadButtons];
3145 /* Package Table {{{ */
3146 @interface PackageTable : RVPage {
3147 _transient Database *database_;
3151 NSMutableArray *packages_;
3152 NSMutableArray *sections_;
3153 UISectionList *list_;
3156 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3158 - (void) setDelegate:(id)delegate;
3159 - (void) setObject:(id)object;
3161 - (void) reloadData;
3162 - (void) resetCursor;
3164 - (UISectionList *) list;
3166 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3170 @implementation PackageTable
3173 [list_ setDataSource:nil];
3178 [packages_ release];
3179 [sections_ release];
3184 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3185 return [sections_ count];
3188 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3189 return [[sections_ objectAtIndex:section] name];
3192 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3193 return [[sections_ objectAtIndex:section] row];
3196 - (int) numberOfRowsInTable:(UITable *)table {
3197 return [packages_ count];
3200 - (float) table:(UITable *)table heightForRow:(int)row {
3201 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3204 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3206 reusing = [[[PackageCell alloc] init] autorelease];
3207 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3211 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3215 - (void) tableRowSelected:(NSNotification *)notification {
3216 int row = [[notification object] selectedRow];
3220 Package *package = [packages_ objectAtIndex:row];
3221 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3222 [view setDelegate:delegate_];
3223 [view setPackage:package];
3224 [book_ pushPage:view];
3227 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3228 if ((self = [super initWithBook:book]) != nil) {
3229 database_ = database;
3230 title_ = [title retain];
3232 object_ = object == nil ? nil : [object retain];
3234 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3235 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3237 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3238 [list_ setDataSource:self];
3240 UITableColumn *column = [[[UITableColumn alloc]
3241 initWithTitle:@"Name"
3243 width:[self frame].size.width
3246 UITable *table = [list_ table];
3247 [table setSeparatorStyle:1];
3248 [table addTableColumn:column];
3249 [table setDelegate:self];
3250 [table setReusesTableCells:YES];
3252 [self addSubview:list_];
3257 - (void) setDelegate:(id)delegate {
3258 delegate_ = delegate;
3261 - (void) setObject:(id)object {
3267 object_ = [object retain];
3270 - (void) reloadData {
3271 NSArray *packages = [database_ packages];
3273 [packages_ removeAllObjects];
3274 [sections_ removeAllObjects];
3276 for (size_t i(0); i != [packages count]; ++i) {
3277 Package *package([packages objectAtIndex:i]);
3278 if ([[package performSelector:filter_ withObject:object_] boolValue])
3279 [packages_ addObject:package];
3282 Section *section = nil;
3284 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3285 Package *package = [packages_ objectAtIndex:offset];
3286 NSString *name = [package index];
3288 if (section == nil || ![[section name] isEqualToString:name]) {
3289 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3290 [sections_ addObject:section];
3293 [section addToCount];
3299 - (NSString *) title {
3303 - (void) resetViewAnimated:(BOOL)animated {
3304 [list_ resetViewAnimated:animated];
3307 - (void) resetCursor {
3308 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3311 - (UISectionList *) list {
3315 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3316 [list_ setShouldHideHeaderInShortLists:hide];
3322 /* Browser Implementation {{{ */
3323 @implementation BrowserView
3326 WebView *webview = [webview_ webView];
3327 [webview setFrameLoadDelegate:nil];
3328 [webview setResourceLoadDelegate:nil];
3329 [webview setUIDelegate:nil];
3331 [scroller_ setDelegate:nil];
3332 [webview_ setDelegate:nil];
3334 [scroller_ release];
3337 [indicator_ release];
3343 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3344 NSMutableURLRequest *request = [NSMutableURLRequest
3347 timeoutInterval:30.0
3350 [request addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3351 [request addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
3352 [request addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3354 [self loadRequest:request];
3358 - (void) loadURL:(NSURL *)url {
3359 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3362 // XXX: this needs to add the headers
3363 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3367 - (void) loadRequest:(NSURLRequest *)request {
3368 [webview_ loadRequest:request];
3371 - (void) reloadURL {
3372 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3373 [urls_ removeLastObject];
3374 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3377 - (WebView *) webView {
3378 return [webview_ webView];
3381 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3382 [scroller_ setContentSize:frame.size];
3385 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3386 [self view:sender didSetFrame:frame];
3389 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3390 return [self _addHeadersToRequest:request];
3393 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3394 if ([[[request URL] scheme] isEqualToString:@"apptapp"])
3396 [self setBackButtonTitle:title_];
3397 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3398 [browser setDelegate:delegate_];
3399 [book_ pushPage:browser];
3400 [browser loadRequest:[self _addHeadersToRequest:request]];
3401 return [browser webView];
3404 - (void) webView:(WebView *)sender willClickElement:(id)element {
3405 if (![element respondsToSelector:@selector(href)])
3407 NSString *href = [element href];
3410 if ([href hasPrefix:@"apptapp://package/"]) {
3411 NSString *name = [href substringFromIndex:18];
3412 Package *package = [database_ packageWithName:name];
3413 if (package == nil) {
3414 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3415 initWithTitle:@"Cannot Locate Package"
3416 buttons:[NSArray arrayWithObjects:@"Close", nil]
3417 defaultButtonIndex:0
3422 [sheet setBodyText:[NSString stringWithFormat:
3423 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
3426 [sheet popupAlertAnimated:YES];
3428 [self setBackButtonTitle:title_];
3429 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3430 [view setDelegate:delegate_];
3431 [view setPackage:package];
3432 [book_ pushPage:view];
3437 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3438 title_ = [title retain];
3439 [self setTitle:title];
3442 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3443 if ([frame parentFrame] != nil)
3448 [indicator_ startAnimation];
3449 [self reloadButtons];
3451 if (title_ != nil) {
3456 [self setTitle:@"Loading..."];
3458 WebView *webview = [webview_ webView];
3459 NSString *href = [webview mainFrameURL];
3460 [urls_ addObject:[NSURL URLWithString:href]];
3462 CGRect webrect = [scroller_ frame];
3463 webrect.size.height = 0;
3464 [webview_ setFrame:webrect];
3467 - (void) _finishLoading {
3470 [indicator_ stopAnimation];
3471 [self reloadButtons];
3475 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3476 if ([frame parentFrame] != nil)
3478 [self _finishLoading];
3481 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3482 if ([frame parentFrame] != nil)
3484 [self setTitle:[error localizedDescription]];
3485 [self _finishLoading];
3488 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3489 if ((self = [super initWithBook:book]) != nil) {
3490 database_ = database;
3493 struct CGRect bounds = [self bounds];
3495 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3496 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3497 [self addSubview:pinstripe];
3499 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3500 [self addSubview:scroller_];
3502 [scroller_ setScrollingEnabled:YES];
3503 [scroller_ setAdjustForContentSizeChange:YES];
3504 [scroller_ setClipsSubviews:YES];
3505 [scroller_ setAllowsRubberBanding:YES];
3506 [scroller_ setScrollDecelerationFactor:0.99];
3507 [scroller_ setDelegate:self];
3509 CGRect webrect = [scroller_ bounds];
3510 webrect.size.height = 0;
3512 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3513 [scroller_ addSubview:webview_];
3515 [webview_ setTilingEnabled:YES];
3516 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3517 [webview_ setAutoresizes:YES];
3518 [webview_ setDelegate:self];
3519 //[webview_ setEnabledGestures:2];
3521 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3522 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
3523 [indicator_ setStyle:0];
3525 Package *package([database_ packageWithName:@"cydia"]);
3526 NSString *application = package == nil ? @"Cydia" : [NSString
3527 stringWithFormat:@"Cydia/%@",
3531 WebView *webview = [webview_ webView];
3532 [webview setApplicationNameForUserAgent:application];
3533 [webview setFrameLoadDelegate:self];
3534 [webview setResourceLoadDelegate:self];
3535 [webview setUIDelegate:self];
3537 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3541 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3545 - (void) _leftButtonClicked {
3546 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3547 initWithTitle:@"About Cydia Packager"
3548 buttons:[NSArray arrayWithObjects:@"Close", nil]
3549 defaultButtonIndex:0
3555 @"Copyright (C) 2008\n"
3556 "Jay Freeman (saurik)\n"
3557 "saurik@saurik.com\n"
3558 "http://www.saurik.com/\n"
3561 "http://www.theokorigroup.com/\n"
3563 "College of Creative Studies,\n"
3564 "University of California,\n"
3566 "http://www.ccs.ucsb.edu/"
3569 [sheet popupAlertAnimated:YES];
3572 - (void) _rightButtonClicked {
3577 - (NSString *) leftButtonTitle {
3581 - (NSString *) rightButtonTitle {
3582 return loading_ ? @"" : @"Reload";
3585 - (NSString *) title {
3589 - (NSString *) backButtonTitle {
3593 - (void) setPageActive:(BOOL)active {
3595 [book_ addSubview:indicator_];
3597 [indicator_ removeFromSuperview];
3600 - (void) resetViewAnimated:(BOOL)animated {
3606 @interface CYBook : RVBook <
3609 _transient Database *database_;
3611 UIProgressIndicator *indicator_;
3612 UITextLabel *prompt_;
3613 UIProgressBar *progress_;
3617 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3623 /* Install View {{{ */
3624 @interface InstallView : RVPage {
3625 _transient Database *database_;
3626 NSMutableArray *packages_;
3627 NSMutableArray *sections_;
3632 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3633 - (void) reloadData;
3637 @implementation InstallView
3640 [list_ setDataSource:nil];
3641 [list_ setDelegate:nil];
3643 [packages_ release];
3644 [sections_ release];
3646 [accessory_ release];
3650 - (int) numberOfRowsInTable:(UITable *)table {
3651 return [sections_ count] + 1;
3654 - (float) table:(UITable *)table heightForRow:(int)row {
3658 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3660 reusing = [[[SectionCell alloc] init] autorelease];
3661 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3665 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3669 - (void) tableRowSelected:(NSNotification *)notification {
3670 int row = [[notification object] selectedRow];
3681 title = @"All Packages";
3683 section = [sections_ objectAtIndex:(row - 1)];
3684 name = [section name];
3690 title = @"(No Section)";
3694 PackageTable *table = [[[PackageTable alloc]
3698 filter:@selector(isUninstalledInSection:)
3702 [table setDelegate:delegate_];
3704 [book_ pushPage:table];
3707 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3708 if ((self = [super initWithBook:book]) != nil) {
3709 database_ = database;
3711 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3712 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3714 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3715 [self addSubview:list_];
3717 UITableColumn *column = [[[UITableColumn alloc]
3718 initWithTitle:@"Name"
3720 width:[self frame].size.width
3723 [list_ setDataSource:self];
3724 [list_ setSeparatorStyle:1];
3725 [list_ addTableColumn:column];
3726 [list_ setDelegate:self];
3727 [list_ setReusesTableCells:YES];
3733 - (void) reloadData {
3734 NSArray *packages = [database_ packages];
3736 [packages_ removeAllObjects];
3737 [sections_ removeAllObjects];
3739 for (size_t i(0); i != [packages count]; ++i) {
3740 Package *package([packages objectAtIndex:i]);
3741 if ([package valid] && [package installed] == nil)
3742 [packages_ addObject:package];
3745 [packages_ sortUsingSelector:@selector(compareBySection:)];
3747 Section *section = nil;
3748 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3749 Package *package = [packages_ objectAtIndex:offset];
3750 NSString *name = [package section];
3752 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3753 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3754 [sections_ addObject:section];
3757 [section addToCount];
3763 - (void) resetViewAnimated:(BOOL)animated {
3764 [list_ resetViewAnimated:animated];
3767 - (NSString *) title {
3771 - (NSString *) backButtonTitle {
3775 - (UIView *) accessoryView {
3781 /* Changes View {{{ */
3782 @interface ChangesView : RVPage {
3783 _transient Database *database_;
3784 NSMutableArray *packages_;
3785 NSMutableArray *sections_;
3786 UISectionList *list_;
3790 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3791 - (void) reloadData;
3795 @implementation ChangesView
3798 [[list_ table] setDelegate:nil];
3799 [list_ setDataSource:nil];
3801 [packages_ release];
3802 [sections_ release];
3807 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3808 return [sections_ count];
3811 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3812 return [[sections_ objectAtIndex:section] name];
3815 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3816 return [[sections_ objectAtIndex:section] row];
3819 - (int) numberOfRowsInTable:(UITable *)table {
3820 return [packages_ count];
3823 - (float) table:(UITable *)table heightForRow:(int)row {
3824 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3827 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3829 reusing = [[[PackageCell alloc] init] autorelease];
3830 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3834 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3838 - (void) tableRowSelected:(NSNotification *)notification {
3839 int row = [[notification object] selectedRow];
3842 Package *package = [packages_ objectAtIndex:row];
3843 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3844 [view setDelegate:delegate_];
3845 [view setPackage:package];
3846 [book_ pushPage:view];
3849 - (void) _leftButtonClicked {
3850 [(CYBook *)book_ update];
3851 [self reloadButtons];
3854 - (void) _rightButtonClicked {
3855 [delegate_ distUpgrade];
3858 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3859 if ((self = [super initWithBook:book]) != nil) {
3860 database_ = database;
3862 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3863 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3865 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3866 [self addSubview:list_];
3868 [list_ setShouldHideHeaderInShortLists:NO];
3869 [list_ setDataSource:self];
3870 //[list_ setSectionListStyle:1];
3872 UITableColumn *column = [[[UITableColumn alloc]
3873 initWithTitle:@"Name"
3875 width:[self frame].size.width
3878 UITable *table = [list_ table];
3879 [table setSeparatorStyle:1];
3880 [table addTableColumn:column];
3881 [table setDelegate:self];
3882 [table setReusesTableCells:YES];
3888 - (void) reloadData {
3889 NSArray *packages = [database_ packages];
3891 [packages_ removeAllObjects];
3892 [sections_ removeAllObjects];
3894 for (size_t i(0); i != [packages count]; ++i) {
3895 Package *package([packages objectAtIndex:i]);
3896 if ([package installed] == nil && [package valid] || [package upgradable])
3897 [packages_ addObject:package];
3900 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3902 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3903 Section *section = nil;
3906 bool unseens = false;
3908 CFLocaleRef locale = CFLocaleCopyCurrent();
3909 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3911 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3912 Package *package = [packages_ objectAtIndex:offset];
3914 if ([package upgradable]) {
3916 [upgradable addToCount];
3919 NSDate *seen = [package seen];
3924 name = [@"n/a ?" retain];
3926 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3929 if (section == nil || ![[section name] isEqualToString:name]) {
3930 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3931 [sections_ addObject:section];
3935 [section addToCount];
3939 CFRelease(formatter);
3943 Section *last = [sections_ lastObject];
3944 size_t count = [last count];
3945 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3946 [sections_ removeLastObject];
3950 [sections_ insertObject:upgradable atIndex:0];
3953 [self reloadButtons];
3956 - (void) resetViewAnimated:(BOOL)animated {
3957 [list_ resetViewAnimated:animated];
3960 - (NSString *) leftButtonTitle {
3961 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3964 - (NSString *) rightButtonTitle {
3965 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3968 - (NSString *) title {
3974 /* Manage View {{{ */
3975 @interface ManageView : PackageTable {
3978 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3982 @implementation ManageView
3984 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3989 filter:@selector(isInstalledInSection:)
3995 - (NSString *) title {
3996 return @"Installed Packages";
3999 - (NSString *) backButtonTitle {
4000 return @"All Packages";
4005 /* Search View {{{ */
4006 @protocol SearchViewDelegate
4007 - (void) showKeyboard:(BOOL)show;
4010 @interface SearchView : RVPage {
4012 UISearchField *field_;
4013 UITransitionView *transition_;
4014 PackageTable *table_;
4015 UIPreferencesTable *advanced_;
4021 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4022 - (void) reloadData;
4026 @implementation SearchView
4030 [[field_ textTraits] setEditingDelegate:nil];
4032 [field_ setDelegate:nil];
4034 [accessory_ release];
4036 [transition_ release];
4038 [advanced_ release];
4043 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4047 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4049 case 0: return @"Advanced Search (Coming Soon!)";
4051 default: _assert(false);
4055 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4059 default: _assert(false);
4063 - (void) _showKeyboard:(BOOL)show {
4064 CGSize keysize = [UIKeyboard defaultSize];
4065 CGRect keydown = [book_ pageBounds];
4066 CGRect keyup = keydown;
4067 keyup.size.height -= keysize.height - ButtonBarHeight_;
4069 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4071 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4072 [animation setSignificantRectFields:8];
4075 [animation setStartFrame:keydown];
4076 [animation setEndFrame:keyup];
4078 [animation setStartFrame:keyup];
4079 [animation setEndFrame:keydown];
4082 UIAnimator *animator = [UIAnimator sharedAnimator];
4085 addAnimations:[NSArray arrayWithObjects:animation, nil]
4086 withDuration:(KeyboardTime_ - delay)
4091 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4093 [delegate_ showKeyboard:show];
4096 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
4097 [self _showKeyboard:YES];
4100 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
4101 [self _showKeyboard:NO];
4104 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
4106 NSString *text([field_ text]);
4107 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
4113 - (void) textFieldClearButtonPressed:(UITextField *)field {
4117 - (void) keyboardInputShouldDelete:(id)input {
4121 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
4122 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
4126 [field_ resignFirstResponder];
4131 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4132 if ((self = [super initWithBook:book]) != nil) {
4133 CGRect pageBounds = [book_ pageBounds];
4135 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
4136 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4137 [self addSubview:pinstripe];*/
4139 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
4140 [self addSubview:transition_];
4142 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
4144 [advanced_ setReusesTableCells:YES];
4145 [advanced_ setDataSource:self];
4146 [advanced_ reloadData];
4148 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
4149 CGColor dimmed(space_, 0, 0, 0, 0.5);
4150 [dimmed_ setBackgroundColor:dimmed];
4152 table_ = [[PackageTable alloc]
4156 filter:@selector(isSearchedForBy:)
4160 [table_ setShouldHideHeaderInShortLists:NO];
4161 [transition_ transition:0 toView:table_];
4163 CGRect cnfrect = {{1, 38}, {17, 18}};
4166 area.origin.x = cnfrect.size.width + 15;
4168 area.size.width = [self bounds].size.width - area.origin.x - 18;
4169 area.size.height = [UISearchField defaultHeight];
4171 field_ = [[UISearchField alloc] initWithFrame:area];
4173 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
4174 [field_ setFont:font];
4177 [field_ setPlaceholder:@"Package Names & Descriptions"];
4178 [field_ setPaddingTop:5];
4179 [field_ setDelegate:self];
4182 UITextTraits *traits = [field_ textTraits];
4183 [traits setEditingDelegate:self];
4184 [traits setReturnKeyType:6];
4185 [traits setAutoCapsType:0];
4186 [traits setAutoCorrectionType:1];
4189 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
4190 [configure setShowPressFeedback:YES];
4191 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
4192 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
4194 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
4195 [accessory_ addSubview:field_];
4196 [accessory_ addSubview:configure];
4201 LKAnimation *animation = [LKTransition animation];
4202 [animation setType:@"oglFlip"];
4203 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
4204 [animation setFillMode:@"extended"];
4205 [animation setTransitionFlags:3];
4206 [animation setDuration:10];
4207 [animation setSpeed:0.35];
4208 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
4209 [[transition_ _layer] addAnimation:animation forKey:0];
4210 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
4211 flipped_ = !flipped_;
4214 - (void) configurePushed {
4215 [field_ resignFirstResponder];
4219 - (void) resetViewAnimated:(BOOL)animated {
4222 [table_ resetViewAnimated:animated];
4225 - (void) reloadData {
4228 [table_ setObject:[field_ text]];
4229 [table_ reloadData];
4230 [table_ resetCursor];
4233 - (UIView *) accessoryView {
4237 - (NSString *) title {
4241 - (NSString *) backButtonTitle {
4245 - (void) setDelegate:(id)delegate {
4246 [table_ setDelegate:delegate];
4247 [super setDelegate:delegate];
4253 @implementation CYBook
4257 [indicator_ release];
4259 [progress_ release];
4263 - (NSString *) getTitleForPage:(RVPage *)page {
4264 return Simplify([super getTitleForPage:page]);
4272 [navbar_ setPrompt:@""];
4273 [navbar_ addSubview:overlay_];
4274 [indicator_ startAnimation];
4275 [prompt_ setText:@"Updating Database..."];
4276 [progress_ setProgress:0];
4281 detachNewThreadSelector:@selector(_update)
4290 [overlay_ removeFromSuperview];
4291 [indicator_ stopAnimation];
4292 [delegate_ reloadData];
4294 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
4297 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4298 if ((self = [super initWithFrame:frame]) != nil) {
4299 database_ = database;
4302 [navbar_ setBarStyle:1];
4304 CGRect ovrrect = [navbar_ bounds];
4305 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
4307 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
4309 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
4310 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4311 CGRect indrect = {{indoffset, indoffset}, indsize};
4313 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
4314 [indicator_ setStyle:(Advanced_ ? 2 : 3)];
4315 [overlay_ addSubview:indicator_];
4317 CGSize prmsize = {200, indsize.width};
4320 indoffset * 2 + indsize.width,
4321 (ovrrect.size.height - prmsize.height) / 2
4324 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
4326 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
4328 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
4329 [prompt_ setBackgroundColor:Clear_];
4330 [prompt_ setFont:font];
4334 [overlay_ addSubview:prompt_];
4336 CGSize prgsize = {75, 100};
4339 ovrrect.size.width - prgsize.width - 10,
4340 (ovrrect.size.height - prgsize.height) / 2
4343 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4344 [progress_ setStyle:0];
4345 [overlay_ addSubview:progress_];
4350 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4353 status.setDelegate(self);
4355 [database_ updateWithStatus:status];
4358 performSelectorOnMainThread:@selector(_update_)
4366 - (void) setProgressError:(NSString *)error {
4368 performSelectorOnMainThread:@selector(_setProgressError:)
4374 - (void) setProgressTitle:(NSString *)title {
4376 performSelectorOnMainThread:@selector(_setProgressTitle:)
4382 - (void) setProgressPercent:(float)percent {
4385 - (void) addProgressOutput:(NSString *)output {
4387 performSelectorOnMainThread:@selector(_addProgressOutput:)
4393 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4397 - (void) _setProgressError:(NSString *)error {
4398 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4401 - (void) _setProgressTitle:(NSString *)title {
4402 [prompt_ setText:[title stringByAppendingString:@"..."]];
4405 - (void) _addProgressOutput:(NSString *)output {
4410 @interface Cydia : UIApplication <
4411 ConfirmationViewDelegate,
4412 ProgressViewDelegate,
4421 UIButtonBar *buttonbar_;
4423 ConfirmationView *confirm_;
4425 NSMutableArray *essential_;
4426 NSMutableArray *broken_;
4428 Database *database_;
4429 ProgressView *progress_;
4433 UIKeyboard *keyboard_;
4435 InstallView *install_;
4436 ChangesView *changes_;
4437 ManageView *manage_;
4438 SearchView *search_;
4443 @implementation Cydia
4446 if ([broken_ count] != 0) {
4447 int count = [broken_ count];
4449 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4450 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
4451 buttons:[NSArray arrayWithObjects:
4453 @"Ignore (Temporary)",
4455 defaultButtonIndex:0
4460 [sheet setBodyText:@"When the shell scripts associated with packages fail, they are left in a 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."];
4461 [sheet popupAlertAnimated:YES];
4462 } else if (!Ignored_ && [essential_ count] != 0) {
4463 int count = [essential_ count];
4465 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4466 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
4467 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
4468 defaultButtonIndex:0
4473 [sheet setBodyText:@"One or more essential packages are currently out of date. If these packages are not upgraded you are likely to encounter errors."];
4474 [sheet popupAlertAnimated:YES];
4478 - (void) _reloadData {
4479 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4480 [hud setText:@"Reloading Data"];
4481 [overlay_ addSubview:hud];
4484 [database_ reloadData];
4486 if (Packages_ == nil) {
4487 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:128];
4488 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4493 [essential_ removeAllObjects];
4494 [broken_ removeAllObjects];
4496 NSArray *packages = [database_ packages];
4497 for (int i(0), e([packages count]); i != e; ++i) {
4498 Package *package = [packages objectAtIndex:i];
4500 [broken_ addObject:package];
4501 if ([package upgradable]) {
4502 if ([package essential])
4503 [essential_ addObject:package];
4509 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4510 [buttonbar_ setBadgeValue:badge forButton:3];
4511 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4512 [buttonbar_ setBadgeAnimated:YES forButton:3];
4513 [self setApplicationBadge:badge];
4515 [buttonbar_ setBadgeValue:nil forButton:3];
4516 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4517 [buttonbar_ setBadgeAnimated:NO forButton:3];
4518 [self removeApplicationBadge];
4522 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4526 /* XXX: this is just stupid */
4528 [install_ reloadData];
4530 [changes_ reloadData];
4532 [manage_ reloadData];
4534 [search_ reloadData];
4538 if ([packages count] == 0);
4547 [hud removeFromSuperview];*/
4550 - (void) reloadData {
4551 @synchronized (self) {
4552 if (confirm_ == nil)
4558 pkgProblemResolver *resolver = [database_ resolver];
4560 resolver->InstallProtect();
4561 if (!resolver->Resolve(true))
4566 [database_ prepare];
4568 if ([database_ cache]->BrokenCount() == 0)
4569 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4571 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4572 NSArray *packages = [database_ packages];
4574 for (size_t i(0); i != [packages count]; ++i) {
4575 Package *package = [packages objectAtIndex:i];
4576 if ([package broken])
4577 [broken addObject:[package name]];
4580 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4581 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4582 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4583 defaultButtonIndex:0
4588 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4589 [sheet popupAlertAnimated:YES];
4595 - (void) installPackage:(Package *)package {
4596 @synchronized (self) {
4603 - (void) removePackage:(Package *)package {
4604 @synchronized (self) {
4611 - (void) distUpgrade {
4612 @synchronized (self) {
4613 [database_ upgrade];
4619 @synchronized (self) {
4627 [overlay_ removeFromSuperview];
4631 detachNewThreadSelector:@selector(perform)
4638 - (void) bootstrap_ {
4640 [database_ upgrade];
4641 [database_ prepare];
4642 [database_ perform];
4645 - (void) bootstrap {
4647 detachNewThreadSelector:@selector(bootstrap_)
4650 title:@"Bootstrap Install..."
4654 - (void) progressViewIsComplete:(ProgressView *)progress {
4655 @synchronized (self) {
4658 if (confirm_ != nil) {
4659 [underlay_ addSubview:overlay_];
4660 [confirm_ removeFromSuperview];
4667 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4668 NSString *context = [sheet context];
4669 if ([context isEqualToString:@"fixhalf"])
4672 @synchronized (self) {
4673 for (int i = 0, e = [broken_ count]; i != e; ++i) {
4674 Package *broken = [broken_ objectAtIndex:i];
4677 NSString *id = [broken id];
4678 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
4679 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
4680 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
4681 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
4690 [broken_ removeAllObjects];
4697 else if ([context isEqualToString:@"upgrade"])
4700 @synchronized (self) {
4701 for (int i = 0, e = [essential_ count]; i != e; ++i) {
4702 Package *essential = [essential_ objectAtIndex:i];
4703 [essential install];
4722 - (void) setPage:(RVPage *)page {
4723 [page resetViewAnimated:NO];
4724 [page setDelegate:self];
4725 [book_ setPage:page];
4728 - (RVPage *) _setHomePage {
4729 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4730 [self setPage:browser];
4731 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4735 - (void) buttonBarItemTapped:(id)sender {
4736 unsigned tag = [sender tag];
4738 [book_ resetViewAnimated:YES];
4743 case 1: [self _setHomePage]; break;
4745 case 2: [self setPage:install_]; break;
4746 case 3: [self setPage:changes_]; break;
4747 case 4: [self setPage:manage_]; break;
4748 case 5: [self setPage:search_]; break;
4750 default: _assert(false);
4756 - (void) applicationWillSuspend {
4757 [super applicationWillSuspend];
4762 pid_t pid = ExecFork();
4765 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
4771 - (void) applicationDidFinishLaunching:(id)unused {
4772 _assert(pkgInitConfig(*_config));
4773 _assert(pkgInitSystem(*_config, _system));
4778 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
4779 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
4781 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4782 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4784 [window_ orderFront: self];
4785 [window_ makeKey: self];
4786 [window_ _setHidden: NO];
4788 database_ = [[Database alloc] init];
4789 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
4790 [database_ setDelegate:progress_];
4791 [window_ setContentView:progress_];
4793 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4794 [progress_ setContentView:underlay_];
4796 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4799 [underlay_ addSubview:overlay_];
4801 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4802 0, 0, screenrect.size.width, screenrect.size.height - 48
4803 ) database:database_];
4805 [book_ setDelegate:self];
4807 [overlay_ addSubview:book_];
4809 NSArray *buttonitems = [NSArray arrayWithObjects:
4810 [NSDictionary dictionaryWithObjectsAndKeys:
4811 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4812 @"home-up.png", kUIButtonBarButtonInfo,
4813 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
4814 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4815 self, kUIButtonBarButtonTarget,
4816 @"Home", kUIButtonBarButtonTitle,
4817 @"0", kUIButtonBarButtonType,
4820 [NSDictionary dictionaryWithObjectsAndKeys:
4821 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4822 @"install-up.png", kUIButtonBarButtonInfo,
4823 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4824 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4825 self, kUIButtonBarButtonTarget,
4826 @"Install", kUIButtonBarButtonTitle,
4827 @"0", kUIButtonBarButtonType,
4830 [NSDictionary dictionaryWithObjectsAndKeys:
4831 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4832 @"changes-up.png", kUIButtonBarButtonInfo,
4833 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4834 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4835 self, kUIButtonBarButtonTarget,
4836 @"Changes", kUIButtonBarButtonTitle,
4837 @"0", kUIButtonBarButtonType,
4840 [NSDictionary dictionaryWithObjectsAndKeys:
4841 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4842 @"manage-up.png", kUIButtonBarButtonInfo,
4843 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4844 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4845 self, kUIButtonBarButtonTarget,
4846 @"Manage", kUIButtonBarButtonTitle,
4847 @"0", kUIButtonBarButtonType,
4850 [NSDictionary dictionaryWithObjectsAndKeys:
4851 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4852 @"search-up.png", kUIButtonBarButtonInfo,
4853 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4854 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4855 self, kUIButtonBarButtonTarget,
4856 @"Search", kUIButtonBarButtonTitle,
4857 @"0", kUIButtonBarButtonType,
4861 buttonbar_ = [[UIButtonBar alloc]
4863 withFrame:CGRectMake(
4864 0, screenrect.size.height - ButtonBarHeight_,
4865 screenrect.size.width, ButtonBarHeight_
4867 withItemList:buttonitems
4870 [buttonbar_ setDelegate:self];
4871 [buttonbar_ setBarStyle:1];
4872 [buttonbar_ setButtonBarTrackingMode:2];
4874 int buttons[5] = {1, 2, 3, 4, 5};
4875 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4876 [buttonbar_ showButtonGroup:0 withDuration:0];
4878 for (int i = 0; i != 5; ++i)
4879 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4880 i * 64 + 2, 1, 60, ButtonBarHeight_
4883 [buttonbar_ showSelectionForButton:1];
4884 [overlay_ addSubview:buttonbar_];
4886 [UIKeyboard initImplementationNow];
4887 CGSize keysize = [UIKeyboard defaultSize];
4888 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
4889 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4890 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4891 [overlay_ addSubview:keyboard_];
4893 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
4894 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
4895 manage_ = [[ManageView alloc] initWithBook:book_ database:database_];
4896 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
4898 [progress_ resetView];
4904 [self _setHomePage];
4907 - (void) showKeyboard:(BOOL)show {
4908 CGSize keysize = [UIKeyboard defaultSize];
4909 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
4910 CGRect keyup = keydown;
4911 keyup.origin.y -= keysize.height;
4913 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
4914 [animation setSignificantRectFields:2];
4917 [animation setStartFrame:keydown];
4918 [animation setEndFrame:keyup];
4919 [keyboard_ activate];
4921 [animation setStartFrame:keyup];
4922 [animation setEndFrame:keydown];
4923 [keyboard_ deactivate];
4926 [[UIAnimator sharedAnimator]
4927 addAnimations:[NSArray arrayWithObjects:animation, nil]
4928 withDuration:KeyboardTime_
4933 - (void) slideUp:(UIAlertSheet *)alert {
4935 [alert presentSheetFromButtonBar:buttonbar_];
4937 [alert presentSheetInView:overlay_];
4942 void AddPreferences(NSString *plist) {
4943 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4945 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4946 _assert(settings != NULL);
4947 NSMutableArray *items = [settings objectForKey:@"items"];
4951 for (size_t i(0); i != [items count]; ++i) {
4952 NSMutableDictionary *item([items objectAtIndex:i]);
4953 NSString *label = [item objectForKey:@"label"];
4954 if (label != nil && [label isEqualToString:@"Cydia"]) {
4961 for (size_t i(0); i != [items count]; ++i) {
4962 NSDictionary *item([items objectAtIndex:i]);
4963 NSString *label = [item objectForKey:@"label"];
4964 if (label != nil && [label isEqualToString:@"General"]) {
4965 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4966 @"CydiaSettings", @"bundle",
4967 @"PSLinkCell", @"cell",
4968 [NSNumber numberWithBool:YES], @"hasIcon",
4969 [NSNumber numberWithBool:YES], @"isController",
4971 nil] atIndex:(i + 1)];
4977 _assert([settings writeToFile:plist atomically:YES] == YES);
4984 id Alloc_(id self, SEL selector) {
4985 id object = alloc_(self, selector);
4986 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4991 id Dealloc_(id self, SEL selector) {
4992 id object = dealloc_(self, selector);
4993 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4997 int main(int argc, char *argv[]) {
4998 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5000 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
5002 Home_ = NSHomeDirectory();
5005 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
5006 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
5007 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
5008 Sounds_Keyboard_ = [keyboard boolValue];
5014 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
5015 alloc_ = alloc->method_imp;
5016 alloc->method_imp = (IMP) &Alloc_;*/
5018 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
5019 dealloc_ = dealloc->method_imp;
5020 dealloc->method_imp = (IMP) &Dealloc_;*/
5022 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
5023 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5024 Firmware_ = strdup([prover UTF8String]);
5025 NSArray *versions = [prover componentsSeparatedByString:@"."];
5026 int count = [versions count];
5027 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
5028 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
5029 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
5034 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
5035 char *machine = new char[size];
5036 sysctlbyname("hw.machine", machine, &size, NULL, 0);
5039 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
5040 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
5041 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
5042 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
5046 IOObjectRelease(service);
5049 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
5050 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
5052 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
5053 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
5055 Packages_ = [Metadata_ objectForKey:@"Packages"];
5057 if (access("/User", F_OK) != 0)
5058 system("/usr/libexec/cydia/firmware.sh");
5060 space_ = CGColorSpaceCreateDeviceRGB();
5062 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
5063 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
5064 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
5065 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
5066 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
5068 int value = UIApplicationMain(argc, argv, [Cydia class]);
5070 CGColorSpaceRelease(space_);