1 /* #include Directives {{{ */
2 #include <Foundation/NSURL.h>
3 #include <UIKit/UIKit.h>
4 #import <GraphicsServices/GraphicsServices.h>
7 #include <ext/stdio_filebuf.h>
9 #include <apt-pkg/acquire.h>
10 #include <apt-pkg/acquire-item.h>
11 #include <apt-pkg/algorithms.h>
12 #include <apt-pkg/cachefile.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/debmetaindex.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/init.h>
17 #include <apt-pkg/pkgrecords.h>
18 #include <apt-pkg/sourcelist.h>
19 #include <apt-pkg/sptr.h>
21 #include <sys/sysctl.h>
24 #include <mach-o/nlist.h>
27 #include <objc/objc-class.h>
33 /* Extension Keywords {{{ */
34 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
36 #define _assert(test) do \
38 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
43 /* Miscellaneous Messages {{{ */
45 - (void) setApplicationNameForUserAgent:(NSString *)applicationName;
48 @interface NSString (Cydia)
49 - (NSString *) stringByAddingPercentEscapes;
50 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
54 /* Reset View (UIView) {{{ */
55 @interface UIView (CYResetView)
56 - (void) resetViewAnimated:(BOOL)animated;
59 @implementation UIView (CYResetView)
61 - (void) resetViewAnimated:(BOOL)animated {
62 fprintf(stderr, "%s\n", self->isa->name);
68 /* Reset View (UITable) {{{ */
69 @interface UITable (CYResetView)
70 - (void) resetViewAnimated:(BOOL)animated;
73 @implementation UITable (CYResetView)
75 - (void) resetViewAnimated:(BOOL)animated {
76 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
81 /* Reset View (UISectionList) {{{ */
82 @interface UISectionList (CYResetView)
83 - (void) resetViewAnimated:(BOOL)animated;
86 @implementation UISectionList (CYResetView)
88 - (void) resetViewAnimated:(BOOL)animated {
89 [[self table] resetViewAnimated:animated];
95 /* Perl-Compatible RegEx {{{ */
105 Pcre(const char *regex) :
110 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
113 fprintf(stderr, "%d:%s\n", offset, error);
117 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
118 matches_ = new int[(capture_ + 1) * 3];
126 NSString *operator [](size_t match) {
128 stringWithCString:(data_ + matches_[match * 2])
129 length:(matches_[match * 2 + 1] - matches_[match * 2])
133 bool operator ()(const char *data, size_t size) {
135 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
139 /* CoreGraphicsServices Primitives {{{ */
145 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
146 float color[] = {red, green, blue, alpha};
147 color_ = CGColorCreate(space, color);
151 CGColorRelease(color_);
154 operator CGColorRef() {
165 /* XXX: no GSFontRelease()? */
171 static const int PulseInterval_ = 50000;
172 const char *Machine_ = NULL;
173 const char *SerialNumber_ = NULL;
175 static NSMutableDictionary *Metadata_;
176 static NSMutableDictionary *Packages_;
179 @protocol ProgressDelegate
180 - (void) setError:(NSString *)error;
181 - (void) setTitle:(NSString *)title;
182 - (void) setPercent:(float)percent;
183 - (void) addOutput:(NSString *)output;
186 NSString *SizeString(double size) {
188 while (size > 1024) {
193 static const char *powers_[] = {"B", "kB", "MB", "GB"};
195 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
198 static const float TextViewOffset_ = 22;
200 UITextView *GetTextView(NSString *value, float left, bool html) {
201 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
202 [text setEditable:NO];
203 [text setTextSize:16];
205 [text setHTML:value];
207 [text setText:value];
208 [text setEnabled:NO];
210 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
211 CGColor clear(space, 0, 0, 0, 0);
212 [text setBackgroundColor:clear];
213 CGColorSpaceRelease(space);
215 CGRect frame = [text frame];
216 [text setFrame:frame];
217 CGRect rect = [text visibleTextRect];
218 frame.size.height = rect.size.height;
219 [text setFrame:frame];
224 /* Status Delegation {{{ */
226 public pkgAcquireStatus
237 void setDelegate(id delegate) {
238 delegate_ = delegate;
241 virtual bool MediaChange(std::string media, std::string drive) {
245 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
248 virtual void Fetch(pkgAcquire::ItemDesc &item) {
249 [delegate_ setTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
252 virtual void Done(pkgAcquire::ItemDesc &item) {
255 virtual void Fail(pkgAcquire::ItemDesc &item) {
257 item.Owner->Status == pkgAcquire::Item::StatIdle ||
258 item.Owner->Status == pkgAcquire::Item::StatDone
262 [delegate_ setError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
265 virtual bool Pulse(pkgAcquire *Owner) {
266 bool value = pkgAcquireStatus::Pulse(Owner);
269 double(CurrentBytes + CurrentItems) /
270 double(TotalBytes + TotalItems)
273 [delegate_ setPercent:percent];
277 virtual void Start() {
280 virtual void Stop() {
284 /* Progress Delegation {{{ */
292 virtual void Update() {
293 [delegate_ setTitle:[NSString stringWithCString:Op.c_str()]];
294 [delegate_ setPercent:(Percent / 100)];
303 void setDelegate(id delegate) {
304 delegate_ = delegate;
307 virtual void Done() {
308 [delegate_ setPercent:1];
313 /* External Constants {{{ */
314 extern NSString *kUIButtonBarButtonAction;
315 extern NSString *kUIButtonBarButtonInfo;
316 extern NSString *kUIButtonBarButtonInfoOffset;
317 extern NSString *kUIButtonBarButtonSelectedInfo;
318 extern NSString *kUIButtonBarButtonStyle;
319 extern NSString *kUIButtonBarButtonTag;
320 extern NSString *kUIButtonBarButtonTarget;
321 extern NSString *kUIButtonBarButtonTitle;
322 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
323 extern NSString *kUIButtonBarButtonTitleWidth;
324 extern NSString *kUIButtonBarButtonType;
326 /* Mime Addresses {{{ */
327 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
329 @interface Address : NSObject {
337 - (NSString *) email;
339 + (Address *) addressWithString:(NSString *)string;
340 - (Address *) initWithString:(NSString *)string;
343 @implementation Address
352 - (NSString *) name {
356 - (NSString *) email {
360 + (Address *) addressWithString:(NSString *)string {
361 return [[[Address alloc] initWithString:string] autorelease];
364 - (Address *) initWithString:(NSString *)string {
365 if ((self = [super init]) != nil) {
366 const char *data = [string UTF8String];
367 size_t size = [string length];
369 if (email_r(data, size)) {
370 name_ = [email_r[1] retain];
371 email_ = [email_r[2] retain];
373 name_ = [[NSString stringWithCString:data length:size] retain];
382 /* Right Alignment {{{ */
383 @interface UIRightTextLabel : UITextLabel {
384 float _savedRightEdgeX;
385 BOOL _sizedtofit_flag;
388 - (void) setFrame:(CGRect)frame;
389 - (void) setText:(NSString *)text;
390 - (void) realignText;
393 @implementation UIRightTextLabel
395 - (void) setFrame:(CGRect)frame {
396 [super setFrame:frame];
397 if (_sizedtofit_flag == NO) {
398 _savedRightEdgeX = frame.origin.x;
403 - (void) setText:(NSString *)text {
404 [super setText:text];
408 - (void) realignText {
409 CGRect oldFrame = [self frame];
411 _sizedtofit_flag = YES;
412 [self sizeToFit]; // shrink down size so I can right align it
414 CGRect newFrame = [self frame];
416 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
417 oldFrame.size.width = newFrame.size.width;
418 [super setFrame:oldFrame];
419 _sizedtofit_flag = NO;
424 /* Linear Algebra {{{ */
425 inline float interpolate(float begin, float end, float fraction) {
426 return (end - begin) * fraction + begin;
432 /* Database Interface {{{ */
433 @interface Database : NSObject {
435 pkgRecords *records_;
436 pkgProblemResolver *resolver_;
437 pkgAcquire *fetcher_;
439 SPtr<pkgPackageManager> manager_;
449 - (void) _readStatus:(NSNumber *)fd;
450 - (void) _readOutput:(NSNumber *)fd;
452 - (Package *) packageWithName:(NSString *)name;
455 - (pkgCacheFile &) cache;
456 - (pkgRecords *) records;
457 - (pkgProblemResolver *) resolver;
458 - (pkgAcquire &) fetcher;
466 - (void) setDelegate:(id)delegate;
471 @interface ResetView : UIView {
472 UIPushButton *reload_;
473 NSMutableArray *views_;
474 UINavigationBar *navbar_;
475 UITransitionView *transition_;
482 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
484 - (id) initWithFrame:(CGRect)frame;
485 - (void) setDelegate:(id)delegate;
486 - (void) reloadPushed;
488 - (void) pushView:(UIView *)view withTitle:(NSString *)title backButtonTitle:(NSString *)back rightButton:(NSString *)right;
489 - (void) popViews:(unsigned)views;
495 @implementation ResetView
499 [transition_ release];
505 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
506 [views_ removeLastObject];
507 UIView *view([views_ lastObject]);
508 [view resetViewAnimated:!resetting_];
510 [transition_ transition:2 toView:view];
512 if ([views_ count] == 1)
516 - (id) initWithFrame:(CGRect)frame {
517 if ((self = [super initWithFrame:frame]) != nil) {
518 views_ = [[NSMutableArray arrayWithCapacity:4] retain];
520 struct CGRect bounds = [self bounds];
521 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
522 CGRect navrect = {{0, 0}, navsize};
524 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
525 [self addSubview:navbar_];
527 [navbar_ setBarStyle:1];
528 [navbar_ setDelegate:self];
530 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
531 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
534 //reload_ = [[UIPushButton alloc] initWithFrame:CGRectMake(284, 8, 29, 23)];
535 reload_ = [[UIPushButton alloc] initWithFrame:CGRectMake(282, 5, 29, 23)];
536 [reload_ setShowPressFeedback:YES];
537 [reload_ setImage:[UIImage applicationImageNamed:@"reload.png"]];
538 [reload_ addTarget:self action:@selector(reloadPushed) forEvents:1];
540 [navbar_ addSubview:reload_];
542 [self addSubview:transition_];
546 - (void) setDelegate:(id)delegate {
547 delegate_ = delegate;
550 - (void) reloadPushed {
554 - (void) pushView:(UIView *)view withTitle:(NSString *)title backButtonTitle:(NSString *)back rightButton:(NSString *)right {
555 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
556 [navbar_ pushNavigationItem:navitem];
557 [navitem setBackButtonTitle:back];
559 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
561 [transition_ transition:([views_ count] == 0 ? 0 : 1) toView:view];
562 [views_ addObject:view];
565 - (void) popViews:(unsigned)views {
567 for (unsigned i(0); i != views; ++i)
568 [navbar_ popNavigationItem];
571 [transition_ transition:2 toView:[views_ lastObject]];
576 if ([[navbar_ navigationItems] count] == 1)
579 [navbar_ popNavigationItem];
580 while ([[navbar_ navigationItems] count] != 1);
583 [transition_ transition:0 toView:[views_ lastObject]];
586 - (void) _resetView {
590 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
592 CFLocaleRef locale = CFLocaleCopyCurrent();
593 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
594 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
596 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", (NSString *) formatted]];
598 CFRelease(formatter);
599 CFRelease(formatted);
605 /* Confirmation View {{{ */
606 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
607 if ([packages count] == 0)
610 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 110, false);
611 [fields setObject:text forKey:key];
613 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
614 CGColor blue(space, 0, 0, 0.4, 1);
615 [text setTextColor:blue];
616 CGColorSpaceRelease(space);
619 @protocol ConfirmationViewDelegate
624 @interface ConfirmationView : UIView {
627 UITransitionView *transition_;
629 UINavigationBar *navbar_;
630 UIPreferencesTable *table_;
631 NSMutableDictionary *fields_;
632 UIAlertSheet *essential_;
638 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
639 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
640 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
642 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
643 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
644 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
645 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
646 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
648 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
652 @implementation ConfirmationView
655 [transition_ release];
660 if (essential_ != nil)
661 [essential_ release];
666 [transition_ transition:7 toView:nil];
670 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
671 if (from != nil && to == nil)
672 [self removeFromSuperview];
675 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
678 if (essential_ != nil)
679 [essential_ popupAlertAnimated:YES];
690 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
691 [essential_ dismiss];
695 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
699 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
701 case 0: return @"Statistics";
702 case 1: return @"Modifications";
704 default: _assert(false);
708 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
711 case 1: return [fields_ count];
713 default: _assert(false);
717 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
718 if (group != 1 || row == -1)
721 _assert(size_t(row) < [fields_ count]);
722 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
726 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
727 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
728 [cell setShowSelection:NO];
731 case 0: switch (row) {
733 [cell setTitle:@"Downloading"];
734 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
738 [cell setTitle:@"Resuming At"];
739 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
743 double size([database_ cache]->UsrSize());
746 [cell setTitle:@"Disk Freeing"];
747 [cell setValue:SizeString(-size)];
749 [cell setTitle:@"Disk Using"];
750 [cell setValue:SizeString(size)];
754 default: _assert(false);
758 _assert(size_t(row) < [fields_ count]);
759 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
760 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
763 default: _assert(false);
769 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
770 if ((self = [super initWithFrame:[view bounds]]) != nil) {
771 database_ = database;
772 delegate_ = delegate;
774 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
775 [self addSubview:transition_];
777 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
779 CGSize navsize = [UINavigationBar defaultSize];
780 CGRect navrect = {{0, 0}, navsize};
781 CGRect bounds = [overlay_ bounds];
783 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
784 [navbar_ setBarStyle:1];
785 [navbar_ setDelegate:self];
787 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
788 [navbar_ pushNavigationItem:navitem];
789 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
791 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
793 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
794 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
795 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
797 bool essential(false);
799 pkgCacheFile &cache([database_ cache]);
800 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
801 NSString *name([NSString stringWithCString:iterator.Name()]);
802 if (cache[iterator].NewInstall())
803 [installing addObject:name];
804 else if (cache[iterator].Upgrade())
805 [upgrading addObject:name];
806 else if (cache[iterator].Delete()) {
807 [removing addObject:name];
808 if ((iterator->Flags & pkgCache::Flag::Essential) != 0)
816 essential_ = [[UIAlertSheet alloc]
817 initWithTitle:@"Unable to Comply"
818 buttons:[NSArray arrayWithObjects:@"Okay", nil]
824 [essential_ setBodyText:@"One or more of the packages you are about to remove are marked 'Essential' and cannot be removed by Cydia. Please use apt-get."];
827 AddTextView(fields_, installing, @"Installing");
828 AddTextView(fields_, upgrading, @"Upgrading");
829 AddTextView(fields_, removing, @"Removing");
831 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
832 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
835 [table_ setReusesTableCells:YES];
836 [table_ setDataSource:self];
839 [overlay_ addSubview:navbar_];
840 [overlay_ addSubview:table_];
842 [view addSubview:self];
844 [transition_ setDelegate:self];
846 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
847 [transition_ transition:0 toView:blank];
848 [transition_ transition:3 toView:overlay_];
855 /* Package Class {{{ */
856 NSString *Scour(const char *field, const char *begin, const char *end) {
857 size_t i(0), l(strlen(field));
860 const char *name = begin + i;
861 const char *colon = name + l;
862 const char *value = colon + 1;
867 memcmp(name, field, l) == 0
869 while (value != end && value[0] == ' ')
871 const char *line = std::find(value, end, '\n');
872 while (line != value && line[-1] == ' ')
874 return [NSString stringWithCString:value length:(line - value)];
876 begin = std::find(begin, end, '\n');
884 @interface Package : NSObject {
885 pkgCache::PkgIterator iterator_;
887 pkgCache::VerIterator version_;
888 pkgCache::VerFileIterator file_;
891 NSString *installed_;
902 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
903 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
905 - (NSString *) section;
906 - (Address *) maintainer;
908 - (NSString *) description;
909 - (NSString *) index;
913 - (NSString *) latest;
914 - (NSString *) installed;
919 - (NSString *) tagline;
921 - (NSString *) bundle;
923 - (BOOL) matches:(NSString *)text;
925 - (NSComparisonResult) compareByName:(Package *)package;
926 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
927 - (NSComparisonResult) compareForChanges:(Package *)package;
933 @implementation Package
937 if (installed_ != nil)
938 [installed_ release];
951 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
952 if ((self = [super init]) != nil) {
953 iterator_ = iterator;
954 database_ = database;
959 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
961 const char *begin, *end;
962 parser->GetRec(begin, end);
964 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
965 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
967 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
968 name_ = Scour("Name", begin, end);
970 name_ = [name_ retain];
971 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
972 icon_ = Scour("Icon", begin, end);
974 icon_ = [icon_ retain];
975 bundle_ = Scour("Bundle", begin, end);
977 bundle_ = [bundle_ retain];
979 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
980 if (metadata == nil) {
981 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
985 [Packages_ setObject:metadata forKey:id_];
990 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
991 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
992 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
993 return [[[Package alloc]
994 initWithIterator:iterator
1002 - (NSString *) section {
1003 return [[NSString stringWithCString:iterator_.Section()] stringByReplacingCharacter:'_' withCharacter:' '];
1006 - (Address *) maintainer {
1007 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1008 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
1012 return version_->InstalledSize;
1015 - (NSString *) description {
1016 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1017 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
1019 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1020 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1021 if ([lines count] < 2)
1024 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1025 for (size_t i(1); i != [lines count]; ++i) {
1026 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1027 [trimmed addObject:trim];
1030 return [trimmed componentsJoinedByString:@"\n"];
1033 - (NSString *) index {
1034 return [[[self name] substringToIndex:1] uppercaseString];
1038 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1041 - (NSString *) latest {
1045 - (NSString *) installed {
1049 - (BOOL) upgradable {
1050 NSString *installed = [self installed];
1051 return installed != nil && [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1058 - (NSString *) name {
1059 return name_ == nil ? id_ : name_;
1062 - (NSString *) tagline {
1066 - (NSString *) icon {
1070 - (NSString *) bundle {
1074 - (BOOL) matches:(NSString *)text {
1080 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1081 if (range.location != NSNotFound)
1084 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1085 if (range.location != NSNotFound)
1091 - (NSComparisonResult) compareByName:(Package *)package {
1092 return [[self name] caseInsensitiveCompare:[package name]];
1095 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1096 NSComparisonResult result = [[self section] caseInsensitiveCompare:[package section]];
1097 if (result != NSOrderedSame)
1099 return [self compareByName:package];
1102 - (NSComparisonResult) compareForChanges:(Package *)package {
1103 BOOL lhs = [self upgradable];
1104 BOOL rhs = [package upgradable];
1107 return lhs ? NSOrderedAscending : NSOrderedDescending;
1109 switch ([[self seen] compare:[package seen]]) {
1110 case NSOrderedAscending:
1111 return NSOrderedDescending;
1114 case NSOrderedDescending:
1115 return NSOrderedAscending;
1121 return [self compareByName:package];
1125 pkgProblemResolver *resolver = [database_ resolver];
1126 resolver->Clear(iterator_);
1127 resolver->Protect(iterator_);
1128 [database_ cache]->MarkInstall(iterator_, false);
1132 pkgProblemResolver *resolver = [database_ resolver];
1133 resolver->Clear(iterator_);
1134 resolver->Protect(iterator_);
1135 resolver->Remove(iterator_);
1136 [database_ cache]->MarkDelete(iterator_, true);
1141 /* Section Class {{{ */
1142 @interface Section : NSObject {
1145 NSMutableArray *packages_;
1150 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1151 - (NSString *) name;
1153 - (NSArray *) packages;
1155 - (void) addPackage:(Package *)package;
1158 @implementation Section
1162 [packages_ release];
1166 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1167 if ((self = [super init]) != nil) {
1168 name_ = [name retain];
1170 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1174 - (NSString *) name {
1182 - (NSArray *) packages {
1187 return [packages_ count];
1190 - (void) addPackage:(Package *)package {
1191 [packages_ addObject:package];
1197 /* Package View {{{ */
1198 @protocol PackageViewDelegate
1199 - (void) performPackage:(Package *)package;
1202 @interface PackageView : UIView {
1203 UIPreferencesTable *table_;
1205 UITextView *description_;
1211 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
1212 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
1213 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
1214 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
1215 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
1217 - (BOOL) canSelectRow:(int)row;
1218 - (void) tableRowSelected:(NSNotification *)notification;
1220 - (Package *) package;
1222 - (id) initWithFrame:(struct CGRect)frame;
1223 - (void) setPackage:(Package *)package;
1224 - (void) setDelegate:(id)delegate;
1227 @implementation PackageView
1230 if (package_ != nil)
1232 if (description_ != nil)
1233 [description_ release];
1238 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1242 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1245 case 1: return @"Details";
1246 case 2: return @"Source";
1248 default: _assert(false);
1252 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1253 if (group != 0 || row != 1)
1256 return [description_ visibleTextRect].size.height + TextViewOffset_;
1259 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1265 default: _assert(false);
1269 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1270 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1271 [cell setShowSelection:NO];
1274 case 0: switch (row) {
1276 [cell setTitle:[package_ name]];
1277 [cell setValue:[package_ latest]];
1281 [cell addSubview:description_];
1284 default: _assert(false);
1287 case 1: switch (row) {
1289 [cell setTitle:@"Identifier"];
1290 [cell setValue:[package_ id]];
1294 [cell setTitle:@"Installed Version"];
1295 NSString *installed([package_ installed]);
1296 [cell setValue:(installed == nil ? @"n/a" : installed)];
1300 [cell setTitle:@"Section"];
1301 [cell setValue:[package_ section]];
1305 [cell setTitle:@"Expanded Size"];
1306 [cell setValue:SizeString([package_ size])];
1310 [cell setTitle:@"Maintainer"];
1311 [cell setValue:[[package_ maintainer] name]];
1312 [cell setShowDisclosure:YES];
1313 [cell setShowSelection:YES];
1316 default: _assert(false);
1319 case 2: switch (row) {
1322 default: _assert(false);
1328 - (BOOL) canSelectRow:(int)row {
1332 - (void) tableRowSelected:(NSNotification *)notification {
1333 printf("%d\n", [table_ selectedRow]);
1334 switch ([table_ selectedRow]) {
1336 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
1337 [[package_ maintainer] email],
1338 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
1344 - (Package *) package {
1348 - (id) initWithFrame:(struct CGRect)frame {
1349 if ((self = [super initWithFrame:frame]) != nil) {
1350 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
1351 [self addSubview:table_];
1353 [table_ setDataSource:self];
1354 [table_ setDelegate:self];
1358 - (void) setPackage:(Package *)package {
1359 if (package_ != nil) {
1360 [package_ autorelease];
1364 if (description_ != nil) {
1365 [description_ release];
1369 if (package != nil) {
1370 package_ = [package retain];
1372 NSString *description([package description]);
1373 if (description == nil)
1374 description = [package tagline];
1375 description_ = [GetTextView(description, 12, true) retain];
1377 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1378 CGColor black(space, 0, 0, 0, 1);
1379 [description_ setTextColor:black];
1380 CGColorSpaceRelease(space);
1382 [table_ reloadData];
1386 - (void) setDelegate:(id)delegate {
1387 delegate_ = delegate;
1392 /* Package Cell {{{ */
1393 @interface PackageCell : UITableCell {
1395 UITextLabel *version_;
1396 UITextLabel *description_;
1402 - (PackageCell *) initWithVersioner:(SEL)versioner;
1403 - (void) setPackage:(Package *)package;
1405 - (void) _setSelected:(float)fraction;
1406 - (void) setSelected:(BOOL)selected;
1407 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
1408 - (void) _setSelectionFadeFraction:(float)fraction;
1412 @implementation PackageCell
1417 [description_ release];
1421 - (PackageCell *) initWithVersioner:(SEL)versioner {
1422 if ((self = [super init]) != nil) {
1423 versioner_ = versioner;
1425 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
1426 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
1427 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
1429 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1431 CGColor clear(space, 0, 0, 0, 0);
1433 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
1434 [name_ setBackgroundColor:clear];
1435 [name_ setFont:bold];
1437 version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 7, 70, 25)];
1438 [version_ setBackgroundColor:clear];
1439 [version_ setFont:large];
1441 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
1442 [description_ setBackgroundColor:clear];
1443 [description_ setFont:small];
1445 [self addSubview:name_];
1446 [self addSubview:version_];
1447 [self addSubview:description_];
1449 CGColorSpaceRelease(space);
1457 - (void) setPackage:(Package *)package {
1458 [name_ setText:[package name]];
1459 [version_ setText:[package latest]];
1460 [description_ setText:[package tagline]];
1463 - (void) _setSelected:(float)fraction {
1464 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1466 CGColor black(space,
1467 interpolate(0.0, 1.0, fraction),
1468 interpolate(0.0, 1.0, fraction),
1469 interpolate(0.0, 1.0, fraction),
1473 interpolate(0.2, 1.0, fraction),
1474 interpolate(0.2, 1.0, fraction),
1475 interpolate(1.0, 1.0, fraction),
1479 interpolate(0.4, 1.0, fraction),
1480 interpolate(0.4, 1.0, fraction),
1481 interpolate(0.4, 1.0, fraction),
1484 [name_ setColor:black];
1485 [version_ setColor:blue];
1486 [description_ setColor:gray];
1488 CGColorSpaceRelease(space);
1491 - (void) setSelected:(BOOL)selected {
1492 [self _setSelected:(selected ? 1.0 : 0.0)];
1493 [super setSelected:selected];
1496 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
1498 [self _setSelected:(selected ? 1.0 : 0.0)];
1499 [super setSelected:selected withFade:fade];
1502 - (void) _setSelectionFadeFraction:(float)fraction {
1503 [self _setSelected:fraction];
1504 [super _setSelectionFadeFraction:fraction];
1510 /* Database Implementation {{{ */
1511 @implementation Database
1518 - (void) _readStatus:(NSNumber *)fd {
1519 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1521 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1522 std::istream is(&ib);
1527 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1529 pcre_extra *study = NULL;
1531 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1532 int matches[(capture + 1) * 3];
1534 while (std::getline(is, line)) {
1535 const char *data(line.c_str());
1537 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1539 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1542 [delegate_ setPercent:(percent / 100)];
1544 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1545 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1547 if (type == "pmerror")
1548 [delegate_ setError:string];
1549 else if (type == "pmstatus")
1550 [delegate_ setTitle:string];
1551 else if (type == "pmconffile")
1553 else _assert(false);
1560 - (void) _readOutput:(NSNumber *)fd {
1561 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1563 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1564 std::istream is(&ib);
1567 while (std::getline(is, line))
1568 [delegate_ addOutput:[NSString stringWithCString:line.c_str()]];
1574 - (Package *) packageWithName:(NSString *)name {
1575 pkgCache::PkgIterator iterator(cache_->FindPkg([name cString]));
1576 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1579 - (Database *) init {
1580 if ((self = [super init]) != nil) {
1588 _assert(pipe(fds) != -1);
1592 detachNewThreadSelector:@selector(_readStatus:)
1594 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1597 _assert(pipe(fds) != -1);
1598 _assert(dup2(fds[1], 1) != -1);
1599 _assert(close(fds[1]) != -1);
1602 detachNewThreadSelector:@selector(_readOutput:)
1604 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1609 - (pkgCacheFile &) cache {
1613 - (pkgRecords *) records {
1617 - (pkgProblemResolver *) resolver {
1621 - (pkgAcquire &) fetcher {
1625 - (void) reloadData {
1633 _assert(cache_.Open(progress_, true));
1634 records_ = new pkgRecords(cache_);
1635 resolver_ = new pkgProblemResolver(cache_);
1636 fetcher_ = new pkgAcquire(&status_);
1641 pkgRecords records(cache_);
1643 lock_ = new FileFd();
1644 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1645 _assert(!_error->PendingError());
1648 _assert(list.ReadMainList());
1650 manager_ = (_system->CreatePM(cache_));
1651 _assert(manager_->GetArchives(fetcher_, &list, &records));
1652 _assert(!_error->PendingError());
1656 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1660 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1662 if (result == pkgPackageManager::Failed)
1664 if (_error->PendingError())
1666 if (result != pkgPackageManager::Completed)
1672 _assert(list.ReadMainList());
1675 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1676 _assert(!_error->PendingError());
1678 pkgAcquire fetcher(&status_);
1679 _assert(list.GetIndexes(&fetcher));
1680 _assert(fetcher.Run(PulseInterval_) != pkgAcquire::Failed);
1682 bool failed = false;
1683 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1684 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1685 (*item)->Finished();
1689 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1690 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1691 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1694 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1698 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1699 _assert(pkgApplyStatus(cache_));
1701 if (cache_->BrokenCount() != 0) {
1702 _assert(pkgFixBroken(cache_));
1703 _assert(cache_->BrokenCount() == 0);
1704 _assert(pkgMinimizeUpgrade(cache_));
1707 _assert(pkgDistUpgrade(cache_));
1710 - (void) setDelegate:(id)delegate {
1711 delegate_ = delegate;
1712 status_.setDelegate(delegate);
1713 progress_.setDelegate(delegate);
1719 /* Progress Data {{{ */
1720 @interface ProgressData : NSObject {
1726 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
1733 @implementation ProgressData
1735 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
1736 if ((self = [super init]) != nil) {
1737 selector_ = selector;
1757 /* Progress View {{{ */
1758 @interface ProgressView : UIView <
1762 UIView *background_;
1763 UITransitionView *transition_;
1765 UINavigationBar *navbar_;
1766 UIProgressBar *progress_;
1767 UITextView *output_;
1768 UITextLabel *status_;
1774 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
1775 - (void) setContentView:(UIView *)view;
1778 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
1780 - (void) _retachThread;
1781 - (void) _detachNewThreadData:(ProgressData *)data;
1782 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
1784 - (void) setError:(NSString *)error;
1785 - (void) _setError:(NSString *)error;
1787 - (void) setTitle:(NSString *)title;
1788 - (void) _setTitle:(NSString *)title;
1790 - (void) setPercent:(float)percent;
1791 - (void) _setPercent:(NSNumber *)percent;
1793 - (void) addOutput:(NSString *)output;
1794 - (void) _addOutput:(NSString *)output;
1797 @protocol ProgressViewDelegate
1798 - (void) progressViewIsComplete:(ProgressView *)sender;
1801 @implementation ProgressView
1805 [background_ release];
1806 [transition_ release];
1809 [progress_ release];
1815 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
1816 if ((self = [super initWithFrame:frame]) != nil) {
1817 delegate_ = delegate;
1819 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1821 CGColor black(space, 0.0, 0.0, 0.0, 1.0);
1822 CGColor white(space, 1.0, 1.0, 1.0, 1.0);
1823 CGColor clear(space, 0.0, 0.0, 0.0, 0.0);
1825 background_ = [[UIView alloc] initWithFrame:[self bounds]];
1826 [background_ setBackgroundColor:black];
1827 [self addSubview:background_];
1829 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1830 [self addSubview:transition_];
1832 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1834 CGSize navsize = [UINavigationBar defaultSize];
1835 CGRect navrect = {{0, 0}, navsize};
1837 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1838 [overlay_ addSubview:navbar_];
1840 [navbar_ setBarStyle:1];
1841 [navbar_ setDelegate:self];
1843 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
1844 [navbar_ pushNavigationItem:navitem];
1846 CGRect bounds = [overlay_ bounds];
1847 CGSize prgsize = [UIProgressBar defaultSize];
1850 (bounds.size.width - prgsize.width) / 2,
1851 bounds.size.height - prgsize.height - 20
1854 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
1855 [overlay_ addSubview:progress_];
1857 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
1859 bounds.size.height - prgsize.height - 50,
1860 bounds.size.width - 20,
1864 [status_ setColor:white];
1865 [status_ setBackgroundColor:clear];
1867 [status_ setCentersHorizontally:YES];
1868 //[status_ setFont:font];
1870 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
1872 navrect.size.height + 20,
1873 bounds.size.width - 20,
1874 bounds.size.height - navsize.height - 62 - navrect.size.height
1877 //[output_ setTextFont:@"Courier New"];
1878 [output_ setTextSize:12];
1880 [output_ setTextColor:white];
1881 [output_ setBackgroundColor:clear];
1883 [output_ setMarginTop:0];
1884 [output_ setAllowsRubberBanding:YES];
1886 [overlay_ addSubview:output_];
1887 [overlay_ addSubview:status_];
1889 [progress_ setStyle:0];
1891 CGColorSpaceRelease(space);
1895 - (void) setContentView:(UIView *)view {
1896 view_ = [view retain];
1899 - (void) resetView {
1900 [transition_ transition:6 toView:view_];
1903 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1907 - (void) _retachThread {
1908 [delegate_ progressViewIsComplete:self];
1912 - (void) _detachNewThreadData:(ProgressData *)data {
1913 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1915 [[data target] performSelector:[data selector] withObject:[data object]];
1918 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
1923 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
1924 [navbar_ popNavigationItem];
1925 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1926 [navbar_ pushNavigationItem:navitem];
1928 [status_ setText:nil];
1929 [output_ setText:@""];
1930 [progress_ setProgress:0];
1932 [transition_ transition:6 toView:overlay_];
1935 detachNewThreadSelector:@selector(_detachNewThreadData:)
1937 withObject:[[ProgressData alloc]
1938 initWithSelector:selector
1945 - (void) setError:(NSString *)error {
1947 performSelectorOnMainThread:@selector(_setError:)
1953 - (void) _setError:(NSString *)error {
1954 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
1955 initWithTitle:@"Package Error"
1956 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1957 defaultButtonIndex:0
1962 [sheet setBodyText:error];
1963 [sheet popupAlertAnimated:YES];
1966 - (void) setTitle:(NSString *)title {
1968 performSelectorOnMainThread:@selector(_setTitle:)
1974 - (void) _setTitle:(NSString *)title {
1975 [status_ setText:[title stringByAppendingString:@"..."]];
1978 - (void) setPercent:(float)percent {
1980 performSelectorOnMainThread:@selector(_setPercent:)
1981 withObject:[NSNumber numberWithFloat:percent]
1986 - (void) _setPercent:(NSNumber *)percent {
1987 [progress_ setProgress:[percent floatValue]];
1990 - (void) addOutput:(NSString *)output {
1992 performSelectorOnMainThread:@selector(_addOutput:)
1998 - (void) _addOutput:(NSString *)output {
1999 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2000 CGSize size = [output_ contentSize];
2001 CGRect rect = {{0, size.height}, {size.width, 0}};
2002 [output_ scrollRectToVisible:rect animated:YES];
2008 /* Package Table {{{ */
2009 @protocol PackageTableDelegate
2010 - (void) packageTable:(id)table packageSelected:(Package *)package;
2013 @interface PackageTable : UIView {
2015 UISectionList *list_;
2019 NSMutableArray *sections_;
2024 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
2025 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
2026 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
2028 - (int) numberOfRowsInTable:(UITable *)table;
2029 - (float) table:(UITable *)table heightForRow:(int)row;
2030 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2031 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2032 - (void) tableRowSelected:(NSNotification *)notification;
2034 - (id) initWithFrame:(CGRect)frame versioner:(SEL)versioner;
2036 - (void) setDelegate:(id)delegate;
2037 - (void) setPackages:(NSArray *)packages;
2039 - (void) resetViewAnimated:(BOOL)animated;
2040 - (UITable *) table;
2043 @implementation PackageTable
2047 [sections_ release];
2048 if (packages_ != nil)
2049 [packages_ release];
2053 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2054 return [sections_ count];
2057 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2058 return [[sections_ objectAtIndex:section] name];
2061 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2062 return [[sections_ objectAtIndex:section] row];
2065 - (int) numberOfRowsInTable:(UITable *)table {
2066 return [packages_ count];
2069 - (float) table:(UITable *)table heightForRow:(int)row {
2073 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2075 reusing = [[[PackageCell alloc] initWithVersioner:versioner_] autorelease];
2076 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2080 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2084 - (void) tableRowSelected:(NSNotification *)notification {
2085 int row = [[notification object] selectedRow];
2086 [delegate_ packageTable:self packageSelected:(row == INT_MAX ? nil : [packages_ objectAtIndex:row])];
2089 - (id) initWithFrame:(CGRect)frame versioner:(SEL)versioner {
2090 if ((self = [super initWithFrame:frame]) != nil) {
2091 versioner_ = versioner;
2092 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2094 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2095 [list_ setDataSource:self];
2097 UITableColumn *column = [[[UITableColumn alloc]
2098 initWithTitle:@"Name"
2100 width:frame.size.width
2103 UITable *table = [list_ table];
2104 [table setSeparatorStyle:1];
2105 [table addTableColumn:column];
2106 [table setDelegate:self];
2107 [table setReusesTableCells:YES];
2109 [self addSubview:list_];
2113 - (void) setDelegate:(id)delegate {
2114 delegate_ = delegate;
2117 - (void) setPackages:(NSArray *)packages {
2118 if (packages_ != nil)
2119 [packages_ autorelease];
2120 _assert(packages != nil);
2121 packages_ = [packages retain];
2123 [sections_ removeAllObjects];
2125 Section *section = nil;
2127 for (size_t offset(0); offset != [packages_ count]; ++offset) {
2128 Package *package = [packages_ objectAtIndex:offset];
2129 NSString *name = [package index];
2131 if (section == nil || ![[section name] isEqual:name]) {
2132 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2133 [sections_ addObject:section];
2136 [section addPackage:package];
2142 - (void) resetViewAnimated:(BOOL)animated {
2143 [[list_ table] selectRow:-1 byExtendingSelection:NO withFade:animated];
2146 - (UITable *) table {
2147 return [list_ table];
2153 /* Section Cell {{{ */
2154 @interface SectionCell : UITableCell {
2156 UITextLabel *count_;
2162 - (void) setSection:(Section *)section;
2164 - (void) _setSelected:(float)fraction;
2165 - (void) setSelected:(BOOL)selected;
2166 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2167 - (void) _setSelectionFadeFraction:(float)fraction;
2171 @implementation SectionCell
2180 if ((self = [super init]) != nil) {
2181 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2182 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2184 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2185 CGColor clear(space, 0, 0, 0, 0);
2186 CGColor white(space, 1, 1, 1, 1);
2188 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(47, 9, 250, 25)];
2189 [name_ setBackgroundColor:clear];
2190 [name_ setFont:bold];
2192 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2193 [count_ setCentersHorizontally:YES];
2194 [count_ setBackgroundColor:clear];
2195 [count_ setFont:small];
2196 [count_ setColor:white];
2198 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2199 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2201 [self addSubview:folder];
2202 [self addSubview:name_];
2203 [self addSubview:count_];
2205 [self _setSelected:0];
2207 CGColorSpaceRelease(space);
2214 - (void) setSection:(Section *)section {
2215 if (section == nil) {
2216 [name_ setText:@"All Packages"];
2217 [count_ setText:nil];
2219 [name_ setText:[section name]];
2220 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2224 - (void) _setSelected:(float)fraction {
2225 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2227 CGColor black(space,
2228 interpolate(0.0, 1.0, fraction),
2229 interpolate(0.0, 1.0, fraction),
2230 interpolate(0.0, 1.0, fraction),
2233 [name_ setColor:black];
2235 CGColorSpaceRelease(space);
2238 - (void) setSelected:(BOOL)selected {
2239 [self _setSelected:(selected ? 1.0 : 0.0)];
2240 [super setSelected:selected];
2243 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2245 [self _setSelected:(selected ? 1.0 : 0.0)];
2246 [super setSelected:selected withFade:fade];
2249 - (void) _setSelectionFadeFraction:(float)fraction {
2250 [self _setSelected:fraction];
2251 [super _setSelectionFadeFraction:fraction];
2256 /* Install View {{{ */
2257 @interface InstallView : ResetView <
2258 PackageTableDelegate
2262 PackageTable *table_;
2266 NSMutableArray *packages_;
2271 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2273 - (int) numberOfRowsInTable:(UITable *)table;
2274 - (float) table:(UITable *)table heightForRow:(int)row;
2275 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2276 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2277 - (void) tableRowSelected:(NSNotification *)notification;
2279 - (void) packageTable:(id)table packageSelected:(Package *)package;
2281 - (id) initWithFrame:(CGRect)frame;
2282 - (void) setPackages:(NSArray *)packages;
2283 - (void) setDelegate:(id)delegate;
2286 @implementation InstallView
2289 [packages_ release];
2290 if (sections_ != nil)
2291 [sections_ release];
2298 if (section_ != nil)
2300 if (package_ != nil)
2305 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2307 [[view_ package] install];
2308 [delegate_ resolve];
2309 [delegate_ perform];
2313 - (int) numberOfRowsInTable:(UITable *)table {
2314 return [sections_ count] + 1;
2317 - (float) table:(UITable *)table heightForRow:(int)row {
2321 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2323 reusing = [[[SectionCell alloc] init] autorelease];
2325 [(SectionCell *)reusing setSection:nil];
2327 [(SectionCell *)reusing setSection:[sections_ objectAtIndex:(row - 1)]];
2331 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2335 - (void) tableRowSelected:(NSNotification *)notification {
2336 int row = [[notification object] selectedRow];
2338 if (row == INT_MAX) {
2345 _assert(section_ == nil);
2346 _assert(table_ == nil);
2354 name = @"All Packages";
2356 section = [sections_ objectAtIndex:(row - 1)];
2357 name = [section name];
2358 section_ = [name retain];
2361 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2362 [table_ setDelegate:self];
2363 [table_ setPackages:(section == nil ? packages_ : [section packages])];
2365 [self pushView:table_ withTitle:name backButtonTitle:@"Packages" rightButton:nil];
2369 - (void) packageTable:(id)table packageSelected:(Package *)package {
2370 if (package == nil) {
2377 _assert(package_ == nil);
2378 _assert(view_ == nil);
2380 package_ = [[package name] retain];
2382 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2383 [view_ setDelegate:delegate_];
2385 [view_ setPackage:package];
2387 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Install"];
2391 - (id) initWithFrame:(CGRect)frame {
2392 if ((self = [super initWithFrame:frame]) != nil) {
2393 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2395 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
2396 [self pushView:list_ withTitle:@"Install" backButtonTitle:@"Sections" rightButton:nil];
2398 UITableColumn *column = [[[UITableColumn alloc]
2399 initWithTitle:@"Name"
2401 width:frame.size.width
2404 [list_ setDataSource:self];
2405 [list_ setSeparatorStyle:1];
2406 [list_ addTableColumn:column];
2407 [list_ setDelegate:self];
2408 [list_ setReusesTableCells:YES];
2410 [transition_ transition:0 toView:list_];
2414 - (void) setPackages:(NSArray *)packages {
2415 [packages_ removeAllObjects];
2417 for (size_t i(0); i != [packages count]; ++i) {
2418 Package *package([packages objectAtIndex:i]);
2419 if ([package installed] == nil)
2420 [packages_ addObject:package];
2423 [packages_ sortUsingSelector:@selector(compareBySectionAndName:)];
2424 NSMutableArray *sections = [NSMutableArray arrayWithCapacity:16];
2426 Section *nsection = nil;
2427 Package *npackage = nil;
2429 Section *section = nil;
2430 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2431 Package *package = [packages_ objectAtIndex:offset];
2432 NSString *name = [package section];
2434 if (section == nil || ![[section name] isEqual:name]) {
2435 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2437 if ([name isEqualToString:section_])
2439 [sections addObject:section];
2442 if ([[package name] isEqualToString:package_])
2444 [section addPackage:package];
2447 if (sections_ != nil)
2448 [sections_ release];
2449 sections_ = [sections retain];
2451 [packages_ sortUsingSelector:@selector(compareByName:)];
2457 if (npackage != nil)
2458 [view_ setPackage:npackage];
2459 else if (package_ != nil)
2462 if (nsection != nil)
2463 [table_ setPackages:[nsection packages]];
2464 else if (section_ != nil)
2467 [self popViews:views];
2471 - (void) setDelegate:(id)delegate {
2473 [view_ setDelegate:delegate];
2474 [super setDelegate:delegate];
2479 /* Changes View {{{ */
2480 @interface ChangesView : ResetView <
2481 PackageTableDelegate
2483 UISectionList *list_;
2484 NSMutableArray *packages_;
2485 NSMutableArray *sections_;
2493 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2495 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
2496 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
2497 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
2499 - (int) numberOfRowsInTable:(UITable *)table;
2500 - (float) table:(UITable *)table heightForRow:(int)row;
2501 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2502 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2503 - (void) tableRowSelected:(NSNotification *)notification;
2505 - (id) initWithFrame:(CGRect)frame;
2506 - (void) setPackages:(NSArray *)packages;
2507 - (void) _resetView;
2510 - (void) setDelegate:(id)delegate;
2513 @implementation ChangesView
2517 [packages_ release];
2518 [sections_ release];
2521 if (package_ != nil)
2526 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2527 return [sections_ count];
2530 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2531 return [[sections_ objectAtIndex:section] name];
2534 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2535 return [[sections_ objectAtIndex:section] row];
2538 - (int) numberOfRowsInTable:(UITable *)table {
2539 return [packages_ count];
2542 - (float) table:(UITable *)table heightForRow:(int)row {
2546 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2548 reusing = [[[PackageCell alloc] initWithVersioner:NULL] autorelease];
2549 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2553 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2557 - (void) tableRowSelected:(NSNotification *)notification {
2558 int row = [[notification object] selectedRow];
2559 [self packageTable:self packageSelected:(row == INT_MAX ? nil : [packages_ objectAtIndex:row])];
2562 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2564 [delegate_ upgrade];
2567 - (void) packageTable:(id)table packageSelected:(Package *)package {
2568 if (package == nil) {
2575 _assert(package_ == nil);
2576 _assert(view_ == nil);
2578 package_ = [[package name] retain];
2580 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2581 [view_ setDelegate:delegate_];
2583 [view_ setPackage:package];
2585 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:(
2586 [package upgradable] ? @"Upgrade" : @"Install"
2591 - (id) initWithFrame:(CGRect)frame {
2592 if ((self = [super initWithFrame:frame]) != nil) {
2593 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2594 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2596 list_ = [[UISectionList alloc] initWithFrame:[transition_ bounds] showSectionIndex:NO];
2597 [list_ setShouldHideHeaderInShortLists:NO];
2598 [list_ setDataSource:self];
2599 //[list_ setSectionListStyle:1];
2601 UITableColumn *column = [[[UITableColumn alloc]
2602 initWithTitle:@"Name"
2604 width:frame.size.width
2607 UITable *table = [list_ table];
2608 [table setSeparatorStyle:1];
2609 [table addTableColumn:column];
2610 [table setDelegate:self];
2611 [table setReusesTableCells:YES];
2613 [self pushView:list_ withTitle:@"Changes" backButtonTitle:nil rightButton:nil];
2617 - (void) setPackages:(NSArray *)packages {
2618 [packages_ removeAllObjects];
2619 for (size_t i(0); i != [packages count]; ++i) {
2620 Package *package([packages objectAtIndex:i]);
2621 if ([package installed] == nil || [package upgradable])
2622 [packages_ addObject:package];
2625 [packages_ sortUsingSelector:@selector(compareForChanges:)];
2627 [sections_ removeAllObjects];
2629 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
2630 Section *section = nil;
2633 Package *npackage = nil;
2634 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2635 Package *package = [packages_ objectAtIndex:offset];
2636 if ([[package name] isEqualToString:package_])
2639 if ([package upgradable])
2640 [upgradable addPackage:package];
2642 NSDate *seen = [package seen];
2644 CFLocaleRef locale = CFLocaleCopyCurrent();
2645 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
2646 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
2648 NSString *name = (NSString *) formatted;
2650 if (section == nil || ![[section name] isEqual:name]) {
2651 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2652 [sections_ addObject:section];
2655 [section addPackage:package];
2657 CFRelease(formatter);
2658 CFRelease(formatted);
2663 count_ = [[upgradable packages] count];
2665 [sections_ insertObject:upgradable atIndex:0];
2669 if (npackage != nil)
2670 [view_ setPackage:npackage];
2671 else if (package_ != nil)
2673 if ([views_ count] == 1)
2679 - (void) _resetView {
2680 [navbar_ showButtonsWithLeftTitle:(count_ == 0 ? nil : @"Upgrade All") rightTitle:nil];
2688 - (void) setDelegate:(id)delegate {
2690 [view_ setDelegate:delegate];
2691 [super setDelegate:delegate];
2696 /* Manage View {{{ */
2697 @interface ManageView : ResetView <
2698 PackageTableDelegate
2700 PackageTable *table_;
2707 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2709 - (void) packageTable:(id)table packageSelected:(Package *)package;
2711 - (id) initWithFrame:(CGRect)frame;
2712 - (void) setPackages:(NSArray *)packages;
2714 - (void) setDelegate:(id)delegate;
2717 @implementation ManageView
2723 if (package_ != nil)
2728 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2730 [[view_ package] remove];
2731 [delegate_ resolve];
2732 [delegate_ perform];
2736 - (void) packageTable:(id)table packageSelected:(Package *)package {
2737 if (package == nil) {
2744 _assert(package_ == nil);
2745 _assert(view_ == nil);
2747 package_ = [[package name] retain];
2749 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2750 [view_ setDelegate:delegate_];
2752 [view_ setPackage:package];
2754 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Uninstall"];
2758 - (id) initWithFrame:(CGRect)frame {
2759 if ((self = [super initWithFrame:frame]) != nil) {
2760 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2761 [table_ setDelegate:self];
2763 [self pushView:table_ withTitle:@"Uninstall" backButtonTitle:@"Packages" rightButton:nil];
2767 - (void) setPackages:(NSArray *)packages {
2768 NSMutableArray *local = [NSMutableArray arrayWithCapacity:16];
2769 for (size_t i(0); i != [packages count]; ++i) {
2770 Package *package([packages objectAtIndex:i]);
2771 if ([package installed] != nil)
2772 [local addObject:package];
2775 [local sortUsingSelector:@selector(compareByName:)];
2777 Package *npackage = nil;
2778 for (size_t offset = 0, count = [local count]; offset != count; ++offset) {
2779 Package *package = [local objectAtIndex:offset];
2780 if ([[package name] isEqualToString:package_])
2784 [table_ setPackages:local];
2786 if (npackage != nil)
2787 [view_ setPackage:npackage];
2788 else if (package_ != nil)
2794 - (void) setDelegate:(id)delegate {
2796 [view_ setDelegate:delegate];
2797 [super setDelegate:delegate];
2802 /* Search View {{{ */
2803 @protocol SearchViewDelegate
2804 - (void) showKeyboard:(BOOL)show;
2807 @interface SearchView : ResetView <
2808 PackageTableDelegate
2810 NSMutableArray *packages_;
2812 UISearchField *field_;
2813 PackageTable *table_;
2820 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2821 - (void) packageTable:(id)table packageSelected:(Package *)package;
2823 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field;
2824 - (void) textFieldDidResignFirstResponder:(UITextField *)field;
2826 - (void) keyboardInputChanged:(UIFieldEditor *)editor;
2827 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked;
2829 - (id) initWithFrame:(CGRect)frame;
2830 - (void) setPackages:(NSArray *)packages;
2832 - (void) setDelegate:(id)delegate;
2833 - (void) resetPackage:(Package *)package;
2834 - (void) searchPackages;
2838 @implementation SearchView
2841 [packages_ release];
2842 [accessory_ release];
2847 if (package_ != nil)
2852 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2854 Package *package = [view_ package];
2855 if ([package installed] == nil)
2859 [delegate_ resolve];
2860 [delegate_ perform];
2864 - (void) packageTable:(id)table packageSelected:(Package *)package {
2865 if (package == nil) {
2866 [navbar_ setAccessoryView:accessory_ animate:(resetting_ ? NO : YES) goingBack:YES];
2874 [navbar_ setAccessoryView:nil animate:YES goingBack:NO];
2876 _assert(package_ == nil);
2877 _assert(view_ == nil);
2879 package_ = [[package name] retain];
2881 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2882 [view_ setDelegate:delegate_];
2884 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:nil];
2885 [self resetPackage:package];
2889 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2890 [delegate_ showKeyboard:YES];
2891 [table_ setEnabled:NO];
2893 /*CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2894 CGColor dimmed(alpha, 0, 0, 0, 0.5);
2895 [editor_ setBackgroundColor:dimmed];
2896 CGColorSpaceRelease(space);*/
2899 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
2900 [table_ setEnabled:YES];
2901 [delegate_ showKeyboard:NO];
2904 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
2905 NSString *text([field_ text]);
2906 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
2909 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
2910 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
2913 [self searchPackages];
2914 [field_ resignFirstResponder];
2918 - (id) initWithFrame:(CGRect)frame {
2919 if ((self = [super initWithFrame:frame]) != nil) {
2920 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2922 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2923 [table_ setDelegate:self];
2925 CGRect area = [self bounds];
2928 area.size.width -= 12;
2929 area.size.height = [UISearchField defaultHeight];
2931 field_ = [[UISearchField alloc] initWithFrame:area];
2933 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2934 [field_ setFont:font];
2937 [field_ setPlaceholder:@"Package Names & Descriptions"];
2938 [field_ setPaddingTop:5];
2939 [field_ setDelegate:self];
2941 UITextTraits *traits = [field_ textTraits];
2942 [traits setEditingDelegate:self];
2943 [traits setReturnKeyType:6];
2945 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(6, 6, area.size.width, area.size.height + 30)];
2946 [accessory_ addSubview:field_];
2948 [navbar_ setAccessoryView:accessory_];
2949 [self pushView:table_ withTitle:nil backButtonTitle:@"Search" rightButton:nil];
2951 /* XXX: for the love of god just fix this */
2952 [navbar_ removeFromSuperview];
2953 [self addSubview:navbar_];
2957 - (void) setPackages:(NSArray *)packages {
2958 [packages_ removeAllObjects];
2959 [packages_ addObjectsFromArray:packages];
2960 [packages_ sortUsingSelector:@selector(compareByName:)];
2962 Package *npackage = nil;
2963 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2964 Package *package = [packages_ objectAtIndex:offset];
2965 if ([[package name] isEqualToString:package_])
2969 [self searchPackages];
2971 if (npackage != nil)
2972 [self resetPackage:npackage];
2973 else if (package_ != nil)
2979 - (void) setDelegate:(id)delegate {
2981 [view_ setDelegate:delegate];
2982 [super setDelegate:delegate];
2985 - (void) resetPackage:(Package *)package {
2986 [view_ setPackage:package];
2987 NSString *right = [package installed] == nil ? @"Install" : @"Uninstall";
2988 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
2991 - (void) searchPackages {
2992 NSString *text([field_ text]);
2994 NSMutableArray *packages([NSMutableArray arrayWithCapacity:16]);
2996 for (size_t offset(0), count([packages_ count]); offset != count; ++offset) {
2997 Package *package = [packages_ objectAtIndex:offset];
2998 if ([package matches:text])
2999 [packages addObject:package];
3002 [table_ setPackages:packages];
3003 [[table_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3009 @interface Cydia : UIApplication <
3010 ConfirmationViewDelegate,
3011 ProgressViewDelegate,
3017 UITransitionView *transition_;
3018 UIButtonBar *buttonbar_;
3020 ConfirmationView *confirm_;
3022 Database *database_;
3023 ProgressView *progress_;
3026 UINavigationBar *navbar_;
3027 UIScroller *scroller_;
3028 UIWebView *webview_;
3030 UIProgressIndicator *indicator_;
3032 InstallView *install_;
3033 ChangesView *changes_;
3034 ManageView *manage_;
3035 SearchView *search_;
3039 UIKeyboard *keyboard_;
3043 - (void) reloadData:(BOOL)reset;
3054 - (void) progressViewIsComplete:(ProgressView *)progress;
3056 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
3057 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
3058 - (void) buttonBarItemTapped:(id)sender;
3060 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old;
3062 - (void) applicationWillSuspend;
3063 - (void) applicationDidFinishLaunching:(id)unused;
3066 @implementation Cydia
3069 NSMutableURLRequest *request = [NSMutableURLRequest
3071 cachePolicy:NSURLRequestReloadIgnoringCacheData
3072 timeoutInterval:30.0
3075 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3076 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3078 [webview_ loadRequest:request];
3079 [indicator_ startAnimation];
3082 - (void) reloadData:(BOOL)reset {
3083 [database_ reloadData];
3087 if (Packages_ == nil) {
3088 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
3089 [Metadata_ setObject:Packages_ forKey:@"Packages"];
3092 now_ = [NSDate date];
3094 NSMutableArray *packages = [NSMutableArray arrayWithCapacity:count];
3095 for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
3096 if (Package *package = [Package packageWithIterator:iterator database:database_])
3097 [packages addObject:package];
3099 [install_ setPackages:packages];
3100 [changes_ setPackages:packages];
3101 [manage_ setPackages:packages];
3102 [search_ setPackages:packages];
3105 if (size_t count = [changes_ count]) {
3106 NSString *badge([[NSNumber numberWithInt:count] stringValue]);
3107 [buttonbar_ setBadgeValue:badge forButton:3];
3108 [buttonbar_ setBadgeAnimated:YES forButton:3];
3109 [self setApplicationBadge:badge];
3111 [buttonbar_ setBadgeValue:nil forButton:3];
3112 [buttonbar_ setBadgeAnimated:NO forButton:3];
3113 [self removeApplicationBadge];
3116 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
3119 - (void) setPrompt {
3120 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
3122 CFLocaleRef locale = CFLocaleCopyCurrent();
3123 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3124 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
3126 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", (NSString *) formatted]];
3128 CFRelease(formatter);
3129 CFRelease(formatted);
3134 pkgProblemResolver *resolver = [database_ resolver];
3136 resolver->InstallProtect();
3137 if (!resolver->Resolve(true))
3142 [database_ prepare];
3143 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
3147 [database_ upgrade];
3152 [self reloadData:NO];
3158 [overlay_ removeFromSuperview];
3162 detachNewThreadSelector:@selector(perform)
3171 detachNewThreadSelector:@selector(update)
3174 title:@"Refreshing Sources..."
3178 - (void) progressViewIsComplete:(ProgressView *)progress {
3179 [self reloadData:YES];
3181 if (confirm_ != nil) {
3182 [underlay_ addSubview:overlay_];
3183 [confirm_ removeFromSuperview];
3189 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
3196 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3197 initWithTitle:@"About Cydia Packager"
3198 buttons:[NSArray arrayWithObjects:@"Close", nil]
3199 defaultButtonIndex:0
3205 @"Copyright (C) 2008\n"
3206 "Jay Freeman (saurik)\n"
3207 "saurik@saurik.com\n"
3208 "http://www.saurik.com/\n"
3211 "http://www.theokorigroup.com/\n"
3213 "College of Creative Studies,\n"
3214 "University of California,\n"
3216 "http://www.ccs.ucsb.edu/\n"
3219 "bad_, BHSPitMonkey, cash, Cobra,\n"
3220 "core, Corona, crashx, cromas,\n"
3221 "Darken, dtzWill, Erica, francis,\n"
3222 "Godores, jerry, Kingstone, lounger,\n"
3223 "mbranes, rockabilly, tman, Wbiggs"
3226 [sheet presentSheetFromButtonBar:buttonbar_];
3231 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3235 - (void) buttonBarItemTapped:(id)sender {
3238 switch ([sender tag]) {
3239 case 1: view = featured_; break;
3240 case 2: view = install_; break;
3241 case 3: view = changes_; break;
3242 case 4: view = manage_; break;
3243 case 5: view = search_; break;
3249 if ([view respondsToSelector:@selector(resetView)])
3250 [(id) view resetView];
3251 [transition_ transition:0 toView:view];
3254 - (void) view:(UIView *)view didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3255 [scroller_ setContentSize:frame.size];
3256 [indicator_ stopAnimation];
3259 - (void) applicationWillSuspend {
3261 system("launchctl stop com.apple.SpringBoard");
3264 - (void) applicationDidFinishLaunching:(id)unused {
3265 _assert(pkgInitConfig(*_config));
3266 _assert(pkgInitSystem(*_config, _system));
3272 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
3273 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
3275 [window_ orderFront: self];
3276 [window_ makeKey: self];
3277 [window_ _setHidden: NO];
3279 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
3280 [window_ setContentView:progress_];
3282 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
3283 [progress_ setContentView:underlay_];
3285 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
3286 [underlay_ addSubview:overlay_];
3288 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
3289 0, 0, screenrect.size.width, screenrect.size.height - 48
3292 [overlay_ addSubview:transition_];
3294 featured_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3296 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
3297 CGRect navrect = {{0, 0}, navsize};
3299 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3300 [featured_ addSubview:navbar_];
3302 [navbar_ setBarStyle:1];
3303 [navbar_ setDelegate:self];
3304 [navbar_ setPrompt:@"Welcome to Cydia Packager"];
3306 [navbar_ showButtonsWithLeftTitle:@"About" rightTitle:@"Reload"];
3308 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Featured"] autorelease];
3309 [navbar_ pushNavigationItem:navitem];
3311 struct CGRect subbounds = [featured_ bounds];
3312 subbounds.origin.y += navsize.height;
3313 subbounds.size.height -= navsize.height;
3315 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:subbounds] autorelease];
3316 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3317 [featured_ addSubview:pinstripe];
3319 scroller_ = [[UIScroller alloc] initWithFrame:subbounds];
3320 [featured_ addSubview:scroller_];
3322 [scroller_ setScrollingEnabled:YES];
3323 [scroller_ setAdjustForContentSizeChange:YES];
3324 [scroller_ setClipsSubviews:YES];
3325 [scroller_ setAllowsRubberBanding:YES];
3326 [scroller_ setScrollDecelerationFactor:0.99];
3327 [scroller_ setDelegate:self];
3329 webview_ = [[UIWebView alloc] initWithFrame:[scroller_ bounds]];
3330 [scroller_ addSubview:webview_];
3332 [webview_ setTilingEnabled:YES];
3333 [webview_ setTileSize:CGSizeMake(screenrect.size.width, 500)];
3334 [webview_ setAutoresizes:YES];
3335 [webview_ setDelegate:self];
3337 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3338 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(87, 45, indsize.width, indsize.height)];
3339 [indicator_ setStyle:2];
3340 [featured_ addSubview:indicator_];
3342 NSArray *buttonitems = [NSArray arrayWithObjects:
3343 [NSDictionary dictionaryWithObjectsAndKeys:
3344 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3345 @"featured-up.png", kUIButtonBarButtonInfo,
3346 @"featured-dn.png", kUIButtonBarButtonSelectedInfo,
3347 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
3348 self, kUIButtonBarButtonTarget,
3349 @"Featured", kUIButtonBarButtonTitle,
3350 @"0", kUIButtonBarButtonType,
3353 [NSDictionary dictionaryWithObjectsAndKeys:
3354 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3355 @"install-up.png", kUIButtonBarButtonInfo,
3356 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
3357 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
3358 self, kUIButtonBarButtonTarget,
3359 @"Install", kUIButtonBarButtonTitle,
3360 @"0", kUIButtonBarButtonType,
3363 [NSDictionary dictionaryWithObjectsAndKeys:
3364 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3365 @"changes-up.png", kUIButtonBarButtonInfo,
3366 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
3367 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
3368 self, kUIButtonBarButtonTarget,
3369 @"Changes", kUIButtonBarButtonTitle,
3370 @"0", kUIButtonBarButtonType,
3373 [NSDictionary dictionaryWithObjectsAndKeys:
3374 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3375 @"manage-up.png", kUIButtonBarButtonInfo,
3376 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
3377 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
3378 self, kUIButtonBarButtonTarget,
3379 @"Uninstall", kUIButtonBarButtonTitle,
3380 @"0", kUIButtonBarButtonType,
3383 [NSDictionary dictionaryWithObjectsAndKeys:
3384 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3385 @"search-up.png", kUIButtonBarButtonInfo,
3386 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
3387 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
3388 self, kUIButtonBarButtonTarget,
3389 @"Search", kUIButtonBarButtonTitle,
3390 @"0", kUIButtonBarButtonType,
3394 buttonbar_ = [[UIButtonBar alloc]
3396 withFrame:CGRectMake(
3397 0, screenrect.size.height - 48,
3398 screenrect.size.width, 48
3400 withItemList:buttonitems
3403 [buttonbar_ setDelegate:self];
3404 [buttonbar_ setBarStyle:1];
3405 [buttonbar_ setButtonBarTrackingMode:2];
3407 int buttons[5] = {1, 2, 3, 4, 5};
3408 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
3409 [buttonbar_ showButtonGroup:0 withDuration:0];
3411 for (int i = 0; i != 5; ++i)
3412 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
3413 i * 64 + 2, 1, 60, 48
3416 [buttonbar_ showSelectionForButton:1];
3417 [transition_ transition:0 toView:featured_];
3420 [overlay_ addSubview:buttonbar_];
3423 [UIKeyboard initImplementationNow];
3426 CGRect edtrect = [overlay_ bounds];
3427 edtrect.origin.y += navsize.height;
3428 edtrect.size.height -= navsize.height;
3431 CGSize keysize = [UIKeyboard defaultSize];
3432 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
3433 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
3436 database_ = [[Database alloc] init];
3437 [database_ setDelegate:progress_];
3440 install_ = [[InstallView alloc] initWithFrame:[transition_ bounds]];
3441 [install_ setDelegate:self];
3444 changes_ = [[ChangesView alloc] initWithFrame:[transition_ bounds]];
3445 [changes_ setDelegate:self];
3448 manage_ = [[ManageView alloc] initWithFrame:[transition_ bounds]];
3449 [manage_ setDelegate:self];
3452 search_ = [[SearchView alloc] initWithFrame:[transition_ bounds]];
3453 [search_ setDelegate:self];
3456 [self reloadData:NO];
3458 [progress_ resetView];
3461 Package *package([database_ packageWithName:@"cydia"]);
3462 NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
3463 WebView *webview = [webview_ webView];
3464 [webview setApplicationNameForUserAgent:application];
3467 url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
3472 - (void) showKeyboard:(BOOL)show {
3474 [overlay_ addSubview:keyboard_];
3476 [keyboard_ removeFromSuperview];
3481 void AddPreferences(NSString *plist) {
3482 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3484 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
3485 _assert(settings != NULL);
3486 NSMutableArray *items = [settings objectForKey:@"items"];
3490 for (size_t i(0); i != [items count]; ++i) {
3491 NSMutableDictionary *item([items objectAtIndex:i]);
3492 NSString *label = [item objectForKey:@"label"];
3493 if (label != nil && [label isEqualToString:@"Cydia"]) {
3500 for (size_t i(0); i != [items count]; ++i) {
3501 NSDictionary *item([items objectAtIndex:i]);
3502 NSString *label = [item objectForKey:@"label"];
3503 if (label != nil && [label isEqualToString:@"General"]) {
3504 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
3505 @"CydiaSettings", @"bundle",
3506 @"PSLinkCell", @"cell",
3507 [NSNumber numberWithBool:YES], @"hasIcon",
3508 [NSNumber numberWithBool:YES], @"isController",
3510 nil] atIndex:(i + 1)];
3516 _assert([settings writeToFile:plist atomically:YES] == YES);
3523 id Alloc_(id self, SEL selector) {
3524 id object = alloc_(self, selector);
3525 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
3529 int main(int argc, char *argv[]) {
3531 memset(nl, 0, sizeof(nl));
3532 nl[0].n_un.n_name = "_useMDNSResponder";
3533 nlist("/usr/lib/libc.dylib", nl);
3534 if (nl[0].n_type != N_UNDF)
3535 *(int *) nl[0].n_value = 0;
3540 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
3541 alloc_ = alloc->method_imp;
3542 alloc->method_imp = (IMP) &Alloc_;*/
3544 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3547 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
3548 char *machine = new char[size];
3549 sysctlbyname("hw.machine", machine, &size, NULL, 0);
3552 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
3553 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
3554 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
3555 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
3559 IOObjectRelease(service);
3562 AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
3563 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");
3565 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
3566 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
3568 Packages_ = [Metadata_ objectForKey:@"Packages"];
3570 UIApplicationMain(argc, argv, [Cydia class]);