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 {
2565 [[view_ package] install];
2566 [delegate_ resolve];
2567 [delegate_ perform];
2571 [delegate_ upgrade];
2576 - (void) packageTable:(id)table packageSelected:(Package *)package {
2577 if (package == nil) {
2584 _assert(package_ == nil);
2585 _assert(view_ == nil);
2587 package_ = [[package name] retain];
2589 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2590 [view_ setDelegate:delegate_];
2592 [view_ setPackage:package];
2594 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:(
2595 [package upgradable] ? @"Upgrade" : @"Install"
2600 - (id) initWithFrame:(CGRect)frame {
2601 if ((self = [super initWithFrame:frame]) != nil) {
2602 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2603 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2605 list_ = [[UISectionList alloc] initWithFrame:[transition_ bounds] showSectionIndex:NO];
2606 [list_ setShouldHideHeaderInShortLists:NO];
2607 [list_ setDataSource:self];
2608 //[list_ setSectionListStyle:1];
2610 UITableColumn *column = [[[UITableColumn alloc]
2611 initWithTitle:@"Name"
2613 width:frame.size.width
2616 UITable *table = [list_ table];
2617 [table setSeparatorStyle:1];
2618 [table addTableColumn:column];
2619 [table setDelegate:self];
2620 [table setReusesTableCells:YES];
2622 [self pushView:list_ withTitle:@"Changes" backButtonTitle:nil rightButton:nil];
2626 - (void) setPackages:(NSArray *)packages {
2627 [packages_ removeAllObjects];
2628 for (size_t i(0); i != [packages count]; ++i) {
2629 Package *package([packages objectAtIndex:i]);
2630 if ([package installed] == nil || [package upgradable])
2631 [packages_ addObject:package];
2634 [packages_ sortUsingSelector:@selector(compareForChanges:)];
2636 [sections_ removeAllObjects];
2638 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
2639 Section *section = nil;
2642 Package *npackage = nil;
2643 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2644 Package *package = [packages_ objectAtIndex:offset];
2645 if ([[package name] isEqualToString:package_])
2648 if ([package upgradable])
2649 [upgradable addPackage:package];
2651 NSDate *seen = [package seen];
2653 CFLocaleRef locale = CFLocaleCopyCurrent();
2654 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
2655 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
2657 NSString *name = (NSString *) formatted;
2659 if (section == nil || ![[section name] isEqual:name]) {
2660 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2661 [sections_ addObject:section];
2664 [section addPackage:package];
2666 CFRelease(formatter);
2667 CFRelease(formatted);
2672 count_ = [[upgradable packages] count];
2674 [sections_ insertObject:upgradable atIndex:0];
2678 if (npackage != nil)
2679 [view_ setPackage:npackage];
2680 else if (package_ != nil)
2682 if ([views_ count] == 1)
2688 - (void) _resetView {
2689 [navbar_ showButtonsWithLeftTitle:(count_ == 0 ? nil : @"Upgrade All") rightTitle:nil];
2697 - (void) setDelegate:(id)delegate {
2699 [view_ setDelegate:delegate];
2700 [super setDelegate:delegate];
2705 /* Manage View {{{ */
2706 @interface ManageView : ResetView <
2707 PackageTableDelegate
2709 PackageTable *table_;
2716 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2718 - (void) packageTable:(id)table packageSelected:(Package *)package;
2720 - (id) initWithFrame:(CGRect)frame;
2721 - (void) setPackages:(NSArray *)packages;
2723 - (void) setDelegate:(id)delegate;
2726 @implementation ManageView
2732 if (package_ != nil)
2737 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2739 [[view_ package] remove];
2740 [delegate_ resolve];
2741 [delegate_ perform];
2745 - (void) packageTable:(id)table packageSelected:(Package *)package {
2746 if (package == nil) {
2753 _assert(package_ == nil);
2754 _assert(view_ == nil);
2756 package_ = [[package name] retain];
2758 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2759 [view_ setDelegate:delegate_];
2761 [view_ setPackage:package];
2763 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Uninstall"];
2767 - (id) initWithFrame:(CGRect)frame {
2768 if ((self = [super initWithFrame:frame]) != nil) {
2769 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2770 [table_ setDelegate:self];
2772 [self pushView:table_ withTitle:@"Uninstall" backButtonTitle:@"Packages" rightButton:nil];
2776 - (void) setPackages:(NSArray *)packages {
2777 NSMutableArray *local = [NSMutableArray arrayWithCapacity:16];
2778 for (size_t i(0); i != [packages count]; ++i) {
2779 Package *package([packages objectAtIndex:i]);
2780 if ([package installed] != nil)
2781 [local addObject:package];
2784 [local sortUsingSelector:@selector(compareByName:)];
2786 Package *npackage = nil;
2787 for (size_t offset = 0, count = [local count]; offset != count; ++offset) {
2788 Package *package = [local objectAtIndex:offset];
2789 if ([[package name] isEqualToString:package_])
2793 [table_ setPackages:local];
2795 if (npackage != nil)
2796 [view_ setPackage:npackage];
2797 else if (package_ != nil)
2803 - (void) setDelegate:(id)delegate {
2805 [view_ setDelegate:delegate];
2806 [super setDelegate:delegate];
2811 /* Search View {{{ */
2812 @protocol SearchViewDelegate
2813 - (void) showKeyboard:(BOOL)show;
2816 @interface SearchView : ResetView <
2817 PackageTableDelegate
2819 NSMutableArray *packages_;
2821 UISearchField *field_;
2822 PackageTable *table_;
2829 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2830 - (void) packageTable:(id)table packageSelected:(Package *)package;
2832 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field;
2833 - (void) textFieldDidResignFirstResponder:(UITextField *)field;
2835 - (void) keyboardInputChanged:(UIFieldEditor *)editor;
2836 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked;
2838 - (id) initWithFrame:(CGRect)frame;
2839 - (void) setPackages:(NSArray *)packages;
2841 - (void) setDelegate:(id)delegate;
2842 - (void) resetPackage:(Package *)package;
2843 - (void) searchPackages;
2847 @implementation SearchView
2850 [packages_ release];
2851 [accessory_ release];
2856 if (package_ != nil)
2861 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2863 Package *package = [view_ package];
2864 if ([package installed] == nil)
2868 [delegate_ resolve];
2869 [delegate_ perform];
2873 - (void) packageTable:(id)table packageSelected:(Package *)package {
2874 if (package == nil) {
2875 [navbar_ setAccessoryView:accessory_ animate:(resetting_ ? NO : YES) goingBack:YES];
2883 [navbar_ setAccessoryView:nil animate:YES goingBack:NO];
2885 _assert(package_ == nil);
2886 _assert(view_ == nil);
2888 package_ = [[package name] retain];
2890 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2891 [view_ setDelegate:delegate_];
2893 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:nil];
2894 [self resetPackage:package];
2898 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2899 [delegate_ showKeyboard:YES];
2900 [table_ setEnabled:NO];
2902 /*CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2903 CGColor dimmed(alpha, 0, 0, 0, 0.5);
2904 [editor_ setBackgroundColor:dimmed];
2905 CGColorSpaceRelease(space);*/
2908 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
2909 [table_ setEnabled:YES];
2910 [delegate_ showKeyboard:NO];
2913 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
2914 NSString *text([field_ text]);
2915 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
2918 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
2919 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
2922 [self searchPackages];
2923 [field_ resignFirstResponder];
2927 - (id) initWithFrame:(CGRect)frame {
2928 if ((self = [super initWithFrame:frame]) != nil) {
2929 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2931 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2932 [table_ setDelegate:self];
2934 CGRect area = [self bounds];
2937 area.size.width -= 12;
2938 area.size.height = [UISearchField defaultHeight];
2940 field_ = [[UISearchField alloc] initWithFrame:area];
2942 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2943 [field_ setFont:font];
2946 [field_ setPlaceholder:@"Package Names & Descriptions"];
2947 [field_ setPaddingTop:5];
2948 [field_ setDelegate:self];
2950 UITextTraits *traits = [field_ textTraits];
2951 [traits setEditingDelegate:self];
2952 [traits setReturnKeyType:6];
2954 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(6, 6, area.size.width, area.size.height + 30)];
2955 [accessory_ addSubview:field_];
2957 [navbar_ setAccessoryView:accessory_];
2958 [self pushView:table_ withTitle:nil backButtonTitle:@"Search" rightButton:nil];
2960 /* XXX: for the love of god just fix this */
2961 [navbar_ removeFromSuperview];
2962 [self addSubview:navbar_];
2966 - (void) setPackages:(NSArray *)packages {
2967 [packages_ removeAllObjects];
2968 [packages_ addObjectsFromArray:packages];
2969 [packages_ sortUsingSelector:@selector(compareByName:)];
2971 Package *npackage = nil;
2972 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2973 Package *package = [packages_ objectAtIndex:offset];
2974 if ([[package name] isEqualToString:package_])
2978 [self searchPackages];
2980 if (npackage != nil)
2981 [self resetPackage:npackage];
2982 else if (package_ != nil)
2988 - (void) setDelegate:(id)delegate {
2990 [view_ setDelegate:delegate];
2991 [super setDelegate:delegate];
2994 - (void) resetPackage:(Package *)package {
2995 [view_ setPackage:package];
2996 NSString *right = [package installed] == nil ? @"Install" : @"Uninstall";
2997 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
3000 - (void) searchPackages {
3001 NSString *text([field_ text]);
3003 NSMutableArray *packages([NSMutableArray arrayWithCapacity:16]);
3005 for (size_t offset(0), count([packages_ count]); offset != count; ++offset) {
3006 Package *package = [packages_ objectAtIndex:offset];
3007 if ([package matches:text])
3008 [packages addObject:package];
3011 [table_ setPackages:packages];
3012 [[table_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3018 @interface Cydia : UIApplication <
3019 ConfirmationViewDelegate,
3020 ProgressViewDelegate,
3026 UITransitionView *transition_;
3027 UIButtonBar *buttonbar_;
3029 ConfirmationView *confirm_;
3031 Database *database_;
3032 ProgressView *progress_;
3035 UINavigationBar *navbar_;
3036 UIScroller *scroller_;
3037 UIWebView *webview_;
3039 UIProgressIndicator *indicator_;
3041 InstallView *install_;
3042 ChangesView *changes_;
3043 ManageView *manage_;
3044 SearchView *search_;
3048 UIKeyboard *keyboard_;
3052 - (void) reloadData:(BOOL)reset;
3063 - (void) progressViewIsComplete:(ProgressView *)progress;
3065 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
3066 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
3067 - (void) buttonBarItemTapped:(id)sender;
3069 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old;
3071 - (void) applicationWillSuspend;
3072 - (void) applicationDidFinishLaunching:(id)unused;
3075 @implementation Cydia
3078 NSMutableURLRequest *request = [NSMutableURLRequest
3080 cachePolicy:NSURLRequestReloadIgnoringCacheData
3081 timeoutInterval:30.0
3084 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3085 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3087 [webview_ loadRequest:request];
3088 [indicator_ startAnimation];
3091 - (void) reloadData:(BOOL)reset {
3092 [database_ reloadData];
3096 if (Packages_ == nil) {
3097 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
3098 [Metadata_ setObject:Packages_ forKey:@"Packages"];
3101 now_ = [NSDate date];
3103 NSMutableArray *packages = [NSMutableArray arrayWithCapacity:count];
3104 for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
3105 if (Package *package = [Package packageWithIterator:iterator database:database_])
3106 [packages addObject:package];
3108 [install_ setPackages:packages];
3109 [changes_ setPackages:packages];
3110 [manage_ setPackages:packages];
3111 [search_ setPackages:packages];
3114 if (size_t count = [changes_ count]) {
3115 NSString *badge([[NSNumber numberWithInt:count] stringValue]);
3116 [buttonbar_ setBadgeValue:badge forButton:3];
3117 [buttonbar_ setBadgeAnimated:YES forButton:3];
3118 [self setApplicationBadge:badge];
3120 [buttonbar_ setBadgeValue:nil forButton:3];
3121 [buttonbar_ setBadgeAnimated:NO forButton:3];
3122 [self removeApplicationBadge];
3125 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
3128 - (void) setPrompt {
3129 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
3131 CFLocaleRef locale = CFLocaleCopyCurrent();
3132 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3133 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
3135 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", (NSString *) formatted]];
3137 CFRelease(formatter);
3138 CFRelease(formatted);
3143 pkgProblemResolver *resolver = [database_ resolver];
3145 resolver->InstallProtect();
3146 if (!resolver->Resolve(true))
3151 [database_ prepare];
3152 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
3156 [database_ upgrade];
3161 [self reloadData:NO];
3167 [overlay_ removeFromSuperview];
3171 detachNewThreadSelector:@selector(perform)
3180 detachNewThreadSelector:@selector(update)
3183 title:@"Refreshing Sources..."
3187 - (void) progressViewIsComplete:(ProgressView *)progress {
3188 [self reloadData:YES];
3190 if (confirm_ != nil) {
3191 [underlay_ addSubview:overlay_];
3192 [confirm_ removeFromSuperview];
3198 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
3205 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3206 initWithTitle:@"About Cydia Packager"
3207 buttons:[NSArray arrayWithObjects:@"Close", nil]
3208 defaultButtonIndex:0
3214 @"Copyright (C) 2008\n"
3215 "Jay Freeman (saurik)\n"
3216 "saurik@saurik.com\n"
3217 "http://www.saurik.com/\n"
3220 "http://www.theokorigroup.com/\n"
3222 "College of Creative Studies,\n"
3223 "University of California,\n"
3225 "http://www.ccs.ucsb.edu/\n"
3228 "bad_, BHSPitMonkey, cash, Cobra,\n"
3229 "core, Corona, crashx, cromas,\n"
3230 "Darken, dtzWill, Erica, francis,\n"
3231 "Godores, jerry, Kingstone, lounger,\n"
3232 "mbranes, rockabilly, tman, Wbiggs"
3235 [sheet presentSheetFromButtonBar:buttonbar_];
3240 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3244 - (void) buttonBarItemTapped:(id)sender {
3247 switch ([sender tag]) {
3248 case 1: view = featured_; break;
3249 case 2: view = install_; break;
3250 case 3: view = changes_; break;
3251 case 4: view = manage_; break;
3252 case 5: view = search_; break;
3258 if ([view respondsToSelector:@selector(resetView)])
3259 [(id) view resetView];
3260 [transition_ transition:0 toView:view];
3263 - (void) view:(UIView *)view didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3264 [scroller_ setContentSize:frame.size];
3265 [indicator_ stopAnimation];
3268 - (void) applicationWillSuspend {
3270 system("launchctl stop com.apple.SpringBoard");
3273 - (void) applicationDidFinishLaunching:(id)unused {
3274 _assert(pkgInitConfig(*_config));
3275 _assert(pkgInitSystem(*_config, _system));
3281 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
3282 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
3284 [window_ orderFront: self];
3285 [window_ makeKey: self];
3286 [window_ _setHidden: NO];
3288 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
3289 [window_ setContentView:progress_];
3291 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
3292 [progress_ setContentView:underlay_];
3294 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
3295 [underlay_ addSubview:overlay_];
3297 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
3298 0, 0, screenrect.size.width, screenrect.size.height - 48
3301 [overlay_ addSubview:transition_];
3303 featured_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3305 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
3306 CGRect navrect = {{0, 0}, navsize};
3308 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3309 [featured_ addSubview:navbar_];
3311 [navbar_ setBarStyle:1];
3312 [navbar_ setDelegate:self];
3313 [navbar_ setPrompt:@"Welcome to Cydia Packager"];
3315 [navbar_ showButtonsWithLeftTitle:@"About" rightTitle:@"Reload"];
3317 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Featured"] autorelease];
3318 [navbar_ pushNavigationItem:navitem];
3320 struct CGRect subbounds = [featured_ bounds];
3321 subbounds.origin.y += navsize.height;
3322 subbounds.size.height -= navsize.height;
3324 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:subbounds] autorelease];
3325 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3326 [featured_ addSubview:pinstripe];
3328 scroller_ = [[UIScroller alloc] initWithFrame:subbounds];
3329 [featured_ addSubview:scroller_];
3331 [scroller_ setScrollingEnabled:YES];
3332 [scroller_ setAdjustForContentSizeChange:YES];
3333 [scroller_ setClipsSubviews:YES];
3334 [scroller_ setAllowsRubberBanding:YES];
3335 [scroller_ setScrollDecelerationFactor:0.99];
3336 [scroller_ setDelegate:self];
3338 webview_ = [[UIWebView alloc] initWithFrame:[scroller_ bounds]];
3339 [scroller_ addSubview:webview_];
3341 [webview_ setTilingEnabled:YES];
3342 [webview_ setTileSize:CGSizeMake(screenrect.size.width, 500)];
3343 [webview_ setAutoresizes:YES];
3344 [webview_ setDelegate:self];
3346 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3347 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(87, 45, indsize.width, indsize.height)];
3348 [indicator_ setStyle:2];
3349 [featured_ addSubview:indicator_];
3351 NSArray *buttonitems = [NSArray arrayWithObjects:
3352 [NSDictionary dictionaryWithObjectsAndKeys:
3353 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3354 @"featured-up.png", kUIButtonBarButtonInfo,
3355 @"featured-dn.png", kUIButtonBarButtonSelectedInfo,
3356 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
3357 self, kUIButtonBarButtonTarget,
3358 @"Featured", kUIButtonBarButtonTitle,
3359 @"0", kUIButtonBarButtonType,
3362 [NSDictionary dictionaryWithObjectsAndKeys:
3363 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3364 @"install-up.png", kUIButtonBarButtonInfo,
3365 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
3366 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
3367 self, kUIButtonBarButtonTarget,
3368 @"Install", kUIButtonBarButtonTitle,
3369 @"0", kUIButtonBarButtonType,
3372 [NSDictionary dictionaryWithObjectsAndKeys:
3373 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3374 @"changes-up.png", kUIButtonBarButtonInfo,
3375 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
3376 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
3377 self, kUIButtonBarButtonTarget,
3378 @"Changes", kUIButtonBarButtonTitle,
3379 @"0", kUIButtonBarButtonType,
3382 [NSDictionary dictionaryWithObjectsAndKeys:
3383 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3384 @"manage-up.png", kUIButtonBarButtonInfo,
3385 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
3386 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
3387 self, kUIButtonBarButtonTarget,
3388 @"Uninstall", kUIButtonBarButtonTitle,
3389 @"0", kUIButtonBarButtonType,
3392 [NSDictionary dictionaryWithObjectsAndKeys:
3393 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3394 @"search-up.png", kUIButtonBarButtonInfo,
3395 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
3396 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
3397 self, kUIButtonBarButtonTarget,
3398 @"Search", kUIButtonBarButtonTitle,
3399 @"0", kUIButtonBarButtonType,
3403 buttonbar_ = [[UIButtonBar alloc]
3405 withFrame:CGRectMake(
3406 0, screenrect.size.height - 48,
3407 screenrect.size.width, 48
3409 withItemList:buttonitems
3412 [buttonbar_ setDelegate:self];
3413 [buttonbar_ setBarStyle:1];
3414 [buttonbar_ setButtonBarTrackingMode:2];
3416 int buttons[5] = {1, 2, 3, 4, 5};
3417 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
3418 [buttonbar_ showButtonGroup:0 withDuration:0];
3420 for (int i = 0; i != 5; ++i)
3421 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
3422 i * 64 + 2, 1, 60, 48
3425 [buttonbar_ showSelectionForButton:1];
3426 [transition_ transition:0 toView:featured_];
3429 [overlay_ addSubview:buttonbar_];
3432 [UIKeyboard initImplementationNow];
3435 CGRect edtrect = [overlay_ bounds];
3436 edtrect.origin.y += navsize.height;
3437 edtrect.size.height -= navsize.height;
3440 CGSize keysize = [UIKeyboard defaultSize];
3441 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
3442 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
3445 database_ = [[Database alloc] init];
3446 [database_ setDelegate:progress_];
3449 install_ = [[InstallView alloc] initWithFrame:[transition_ bounds]];
3450 [install_ setDelegate:self];
3453 changes_ = [[ChangesView alloc] initWithFrame:[transition_ bounds]];
3454 [changes_ setDelegate:self];
3457 manage_ = [[ManageView alloc] initWithFrame:[transition_ bounds]];
3458 [manage_ setDelegate:self];
3461 search_ = [[SearchView alloc] initWithFrame:[transition_ bounds]];
3462 [search_ setDelegate:self];
3465 [self reloadData:NO];
3467 [progress_ resetView];
3470 Package *package([database_ packageWithName:@"cydia"]);
3471 NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
3472 WebView *webview = [webview_ webView];
3473 [webview setApplicationNameForUserAgent:application];
3476 url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
3481 - (void) showKeyboard:(BOOL)show {
3483 [overlay_ addSubview:keyboard_];
3485 [keyboard_ removeFromSuperview];
3490 void AddPreferences(NSString *plist) {
3491 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3493 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
3494 _assert(settings != NULL);
3495 NSMutableArray *items = [settings objectForKey:@"items"];
3499 for (size_t i(0); i != [items count]; ++i) {
3500 NSMutableDictionary *item([items objectAtIndex:i]);
3501 NSString *label = [item objectForKey:@"label"];
3502 if (label != nil && [label isEqualToString:@"Cydia"]) {
3509 for (size_t i(0); i != [items count]; ++i) {
3510 NSDictionary *item([items objectAtIndex:i]);
3511 NSString *label = [item objectForKey:@"label"];
3512 if (label != nil && [label isEqualToString:@"General"]) {
3513 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
3514 @"CydiaSettings", @"bundle",
3515 @"PSLinkCell", @"cell",
3516 [NSNumber numberWithBool:YES], @"hasIcon",
3517 [NSNumber numberWithBool:YES], @"isController",
3519 nil] atIndex:(i + 1)];
3525 _assert([settings writeToFile:plist atomically:YES] == YES);
3532 id Alloc_(id self, SEL selector) {
3533 id object = alloc_(self, selector);
3534 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
3538 int main(int argc, char *argv[]) {
3540 memset(nl, 0, sizeof(nl));
3541 nl[0].n_un.n_name = "_useMDNSResponder";
3542 nlist("/usr/lib/libc.dylib", nl);
3543 if (nl[0].n_type != N_UNDF)
3544 *(int *) nl[0].n_value = 0;
3549 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
3550 alloc_ = alloc->method_imp;
3551 alloc->method_imp = (IMP) &Alloc_;*/
3553 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3556 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
3557 char *machine = new char[size];
3558 sysctlbyname("hw.machine", machine, &size, NULL, 0);
3561 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
3562 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
3563 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
3564 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
3568 IOObjectRelease(service);
3571 AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
3572 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");
3574 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
3575 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
3577 Packages_ = [Metadata_ objectForKey:@"Packages"];
3579 UIApplicationMain(argc, argv, [Cydia class]);