]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Forgot to add icon in last commit.
[cydia.git] / Cydia.mm
1 /* #include Directives {{{ */
2 #include <Foundation/NSURL.h>
3 #include <UIKit/UIKit.h>
4 #import <GraphicsServices/GraphicsServices.h>
5
6 #include <sstream>
7 #include <ext/stdio_filebuf.h>
8
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>
20
21 #include <sys/sysctl.h>
22
23 extern "C" {
24 #include <mach-o/nlist.h>
25 }
26
27 #include <objc/objc-class.h>
28
29 #include <errno.h>
30 #include <pcre.h>
31 #include <string.h>
32 /* }}} */
33 /* Extension Keywords {{{ */
34 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
35
36 #define _assert(test) do \
37 if (!(test)) { \
38 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
39 exit(-1); \
40 } \
41 while (false)
42 /* }}} */
43 /* Miscellaneous Messages {{{ */
44 @interface WebView
45 - (void) setApplicationNameForUserAgent:(NSString *)applicationName;
46 @end
47
48 @interface NSString (Cydia)
49 - (NSString *) stringByAddingPercentEscapes;
50 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
51 @end
52 /* }}} */
53
54 /* Reset View (UIView) {{{ */
55 @interface UIView (CYResetView)
56 - (void) resetViewAnimated:(BOOL)animated;
57 @end
58
59 @implementation UIView (CYResetView)
60
61 - (void) resetViewAnimated:(BOOL)animated {
62 fprintf(stderr, "%s\n", self->isa->name);
63 _assert(false);
64 }
65
66 @end
67 /* }}} */
68 /* Reset View (UITable) {{{ */
69 @interface UITable (CYResetView)
70 - (void) resetViewAnimated:(BOOL)animated;
71 @end
72
73 @implementation UITable (CYResetView)
74
75 - (void) resetViewAnimated:(BOOL)animated {
76 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
77 }
78
79 @end
80 /* }}} */
81 /* Reset View (UISectionList) {{{ */
82 @interface UISectionList (CYResetView)
83 - (void) resetViewAnimated:(BOOL)animated;
84 @end
85
86 @implementation UISectionList (CYResetView)
87
88 - (void) resetViewAnimated:(BOOL)animated {
89 [[self table] resetViewAnimated:animated];
90 }
91
92 @end
93 /* }}} */
94
95 /* Perl-Compatible RegEx {{{ */
96 class Pcre {
97 private:
98 pcre *code_;
99 pcre_extra *study_;
100 int capture_;
101 int *matches_;
102 const char *data_;
103
104 public:
105 Pcre(const char *regex) :
106 study_(NULL)
107 {
108 const char *error;
109 int offset;
110 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
111
112 if (code_ == NULL) {
113 fprintf(stderr, "%d:%s\n", offset, error);
114 _assert(false);
115 }
116
117 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
118 matches_ = new int[(capture_ + 1) * 3];
119 }
120
121 ~Pcre() {
122 pcre_free(code_);
123 delete matches_;
124 }
125
126 NSString *operator [](size_t match) {
127 return [NSString
128 stringWithCString:(data_ + matches_[match * 2])
129 length:(matches_[match * 2 + 1] - matches_[match * 2])
130 ];
131 }
132
133 bool operator ()(const char *data, size_t size) {
134 data_ = data;
135 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
136 }
137 };
138 /* }}} */
139 /* CoreGraphicsServices Primitives {{{ */
140 class CGColor {
141 private:
142 CGColorRef color_;
143
144 public:
145 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
146 float color[] = {red, green, blue, alpha};
147 color_ = CGColorCreate(space, color);
148 }
149
150 ~CGColor() {
151 CGColorRelease(color_);
152 }
153
154 operator CGColorRef() {
155 return color_;
156 }
157 };
158
159 class GSFont {
160 private:
161 GSFontRef font_;
162
163 public:
164 ~GSFont() {
165 /* XXX: no GSFontRelease()? */
166 CFRelease(font_);
167 }
168 };
169 /* }}} */
170
171 static const int PulseInterval_ = 50000;
172 const char *Machine_ = NULL;
173 const char *SerialNumber_ = NULL;
174
175 bool bootstrap_ = false;
176
177 static NSMutableDictionary *Metadata_;
178 static NSMutableDictionary *Packages_;
179 static NSDate *now_;
180
181 NSString *GetLastUpdate() {
182 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
183
184 if (update == nil)
185 return @"Never or Unknown";
186
187 CFLocaleRef locale = CFLocaleCopyCurrent();
188 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
189 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
190
191 CFRelease(formatter);
192 CFRelease(formatted);
193 CFRelease(locale);
194
195 return [(NSString *) formatted autorelease];
196 }
197
198 @protocol ProgressDelegate
199 - (void) setError:(NSString *)error;
200 - (void) setTitle:(NSString *)title;
201 - (void) setPercent:(float)percent;
202 - (void) addOutput:(NSString *)output;
203 @end
204
205 NSString *SizeString(double size) {
206 unsigned power = 0;
207 while (size > 1024) {
208 size /= 1024;
209 ++power;
210 }
211
212 static const char *powers_[] = {"B", "kB", "MB", "GB"};
213
214 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
215 }
216
217 static const float TextViewOffset_ = 22;
218
219 UITextView *GetTextView(NSString *value, float left, bool html) {
220 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
221 [text setEditable:NO];
222 [text setTextSize:16];
223 if (html)
224 [text setHTML:value];
225 else
226 [text setText:value];
227 [text setEnabled:NO];
228
229 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
230 CGColor clear(space, 0, 0, 0, 0);
231 [text setBackgroundColor:clear];
232 CGColorSpaceRelease(space);
233
234 CGRect frame = [text frame];
235 [text setFrame:frame];
236 CGRect rect = [text visibleTextRect];
237 frame.size.height = rect.size.height;
238 [text setFrame:frame];
239
240 return text;
241 }
242
243 /* Status Delegation {{{ */
244 class Status :
245 public pkgAcquireStatus
246 {
247 private:
248 id delegate_;
249
250 public:
251 Status() :
252 delegate_(nil)
253 {
254 }
255
256 void setDelegate(id delegate) {
257 delegate_ = delegate;
258 }
259
260 virtual bool MediaChange(std::string media, std::string drive) {
261 return false;
262 }
263
264 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
265 }
266
267 virtual void Fetch(pkgAcquire::ItemDesc &item) {
268 [delegate_ setTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
269 }
270
271 virtual void Done(pkgAcquire::ItemDesc &item) {
272 }
273
274 virtual void Fail(pkgAcquire::ItemDesc &item) {
275 if (
276 item.Owner->Status == pkgAcquire::Item::StatIdle ||
277 item.Owner->Status == pkgAcquire::Item::StatDone
278 )
279 return;
280
281 [delegate_ setError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
282 }
283
284 virtual bool Pulse(pkgAcquire *Owner) {
285 bool value = pkgAcquireStatus::Pulse(Owner);
286
287 float percent(
288 double(CurrentBytes + CurrentItems) /
289 double(TotalBytes + TotalItems)
290 );
291
292 [delegate_ setPercent:percent];
293 return value;
294 }
295
296 virtual void Start() {
297 }
298
299 virtual void Stop() {
300 }
301 };
302 /* }}} */
303 /* Progress Delegation {{{ */
304 class Progress :
305 public OpProgress
306 {
307 private:
308 id delegate_;
309
310 protected:
311 virtual void Update() {
312 [delegate_ setTitle:[NSString stringWithCString:Op.c_str()]];
313 [delegate_ setPercent:(Percent / 100)];
314 }
315
316 public:
317 Progress() :
318 delegate_(nil)
319 {
320 }
321
322 void setDelegate(id delegate) {
323 delegate_ = delegate;
324 }
325
326 virtual void Done() {
327 [delegate_ setPercent:1];
328 }
329 };
330 /* }}} */
331
332 /* External Constants {{{ */
333 extern NSString *kUIButtonBarButtonAction;
334 extern NSString *kUIButtonBarButtonInfo;
335 extern NSString *kUIButtonBarButtonInfoOffset;
336 extern NSString *kUIButtonBarButtonSelectedInfo;
337 extern NSString *kUIButtonBarButtonStyle;
338 extern NSString *kUIButtonBarButtonTag;
339 extern NSString *kUIButtonBarButtonTarget;
340 extern NSString *kUIButtonBarButtonTitle;
341 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
342 extern NSString *kUIButtonBarButtonTitleWidth;
343 extern NSString *kUIButtonBarButtonType;
344 /* }}} */
345 /* Mime Addresses {{{ */
346 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
347
348 @interface Address : NSObject {
349 NSString *name_;
350 NSString *email_;
351 }
352
353 - (void) dealloc;
354
355 - (NSString *) name;
356 - (NSString *) email;
357
358 + (Address *) addressWithString:(NSString *)string;
359 - (Address *) initWithString:(NSString *)string;
360 @end
361
362 @implementation Address
363
364 - (void) dealloc {
365 [name_ release];
366 if (email_ != nil)
367 [email_ release];
368 [super dealloc];
369 }
370
371 - (NSString *) name {
372 return name_;
373 }
374
375 - (NSString *) email {
376 return email_;
377 }
378
379 + (Address *) addressWithString:(NSString *)string {
380 return [[[Address alloc] initWithString:string] autorelease];
381 }
382
383 - (Address *) initWithString:(NSString *)string {
384 if ((self = [super init]) != nil) {
385 const char *data = [string UTF8String];
386 size_t size = [string length];
387
388 if (email_r(data, size)) {
389 name_ = [email_r[1] retain];
390 email_ = [email_r[2] retain];
391 } else {
392 name_ = [[NSString stringWithCString:data length:size] retain];
393 email_ = nil;
394 }
395 } return self;
396 }
397
398 @end
399 /* }}} */
400
401 /* Right Alignment {{{ */
402 @interface UIRightTextLabel : UITextLabel {
403 float _savedRightEdgeX;
404 BOOL _sizedtofit_flag;
405 }
406
407 - (void) setFrame:(CGRect)frame;
408 - (void) setText:(NSString *)text;
409 - (void) realignText;
410 @end
411
412 @implementation UIRightTextLabel
413
414 - (void) setFrame:(CGRect)frame {
415 [super setFrame:frame];
416 if (_sizedtofit_flag == NO) {
417 _savedRightEdgeX = frame.origin.x;
418 [self realignText];
419 }
420 }
421
422 - (void) setText:(NSString *)text {
423 [super setText:text];
424 [self realignText];
425 }
426
427 - (void) realignText {
428 CGRect oldFrame = [self frame];
429
430 _sizedtofit_flag = YES;
431 [self sizeToFit]; // shrink down size so I can right align it
432
433 CGRect newFrame = [self frame];
434
435 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
436 oldFrame.size.width = newFrame.size.width;
437 [super setFrame:oldFrame];
438 _sizedtofit_flag = NO;
439 }
440
441 @end
442 /* }}} */
443 /* Linear Algebra {{{ */
444 inline float interpolate(float begin, float end, float fraction) {
445 return (end - begin) * fraction + begin;
446 }
447 /* }}} */
448
449 @class Package;
450
451 /* Database Interface {{{ */
452 @interface Database : NSObject {
453 pkgCacheFile cache_;
454 pkgRecords *records_;
455 pkgProblemResolver *resolver_;
456 pkgAcquire *fetcher_;
457 FileFd *lock_;
458 SPtr<pkgPackageManager> manager_;
459
460 id delegate_;
461 Status status_;
462 Progress progress_;
463 int statusfd_;
464 }
465
466 - (void) dealloc;
467
468 - (void) _readStatus:(NSNumber *)fd;
469 - (void) _readOutput:(NSNumber *)fd;
470
471 - (Package *) packageWithName:(NSString *)name;
472
473 - (Database *) init;
474 - (pkgCacheFile &) cache;
475 - (pkgRecords *) records;
476 - (pkgProblemResolver *) resolver;
477 - (pkgAcquire &) fetcher;
478 - (void) reloadData;
479
480 - (void) prepare;
481 - (void) perform;
482 - (void) update;
483 - (void) upgrade;
484
485 - (void) setDelegate:(id)delegate;
486 @end
487 /* }}} */
488
489 /* Reset View {{{ */
490 @interface ResetView : UIView {
491 UIPushButton *reload_;
492 NSMutableArray *views_;
493 UINavigationBar *navbar_;
494 UITransitionView *transition_;
495 bool resetting_;
496 id delegate_;
497 }
498
499 - (void) dealloc;
500
501 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
502
503 - (id) initWithFrame:(CGRect)frame;
504 - (void) setDelegate:(id)delegate;
505 - (void) reloadPushed;
506
507 - (void) pushView:(UIView *)view withTitle:(NSString *)title backButtonTitle:(NSString *)back rightButton:(NSString *)right;
508 - (void) popViews:(unsigned)views;
509 - (void) resetView;
510 - (void) _resetView;
511 - (void) setPrompt;
512 @end
513
514 @implementation ResetView
515
516 - (void) dealloc {
517 [reload_ release];
518 [transition_ release];
519 [navbar_ release];
520 [views_ release];
521 [super dealloc];
522 }
523
524 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
525 [views_ removeLastObject];
526 UIView *view([views_ lastObject]);
527 [view resetViewAnimated:!resetting_];
528 if (!resetting_)
529 [transition_ transition:2 toView:view];
530
531 if ([views_ count] == 1)
532 [self _resetView];
533 }
534
535 - (id) initWithFrame:(CGRect)frame {
536 if ((self = [super initWithFrame:frame]) != nil) {
537 views_ = [[NSMutableArray arrayWithCapacity:4] retain];
538
539 struct CGRect bounds = [self bounds];
540 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
541 CGRect navrect = {{0, 0}, navsize};
542
543 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
544 [self addSubview:navbar_];
545
546 [navbar_ setBarStyle:1];
547 [navbar_ setDelegate:self];
548
549 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
550 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
551 )];
552
553 //reload_ = [[UIPushButton alloc] initWithFrame:CGRectMake(284, 8, 29, 23)];
554 reload_ = [[UIPushButton alloc] initWithFrame:CGRectMake(282, 5, 29, 23)];
555 [reload_ setShowPressFeedback:YES];
556 [reload_ setImage:[UIImage applicationImageNamed:@"reload.png"]];
557 [reload_ addTarget:self action:@selector(reloadPushed) forEvents:1];
558
559 [navbar_ addSubview:reload_];
560
561 [self addSubview:transition_];
562 } return self;
563 }
564
565 - (void) setDelegate:(id)delegate {
566 delegate_ = delegate;
567 }
568
569 - (void) reloadPushed {
570 [delegate_ update];
571 }
572
573 - (void) pushView:(UIView *)view withTitle:(NSString *)title backButtonTitle:(NSString *)back rightButton:(NSString *)right {
574 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
575 [navbar_ pushNavigationItem:navitem];
576 [navitem setBackButtonTitle:back];
577
578 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
579
580 [transition_ transition:([views_ count] == 0 ? 0 : 1) toView:view];
581 [views_ addObject:view];
582 }
583
584 - (void) popViews:(unsigned)views {
585 resetting_ = true;
586 for (unsigned i(0); i != views; ++i)
587 [navbar_ popNavigationItem];
588 resetting_ = false;
589
590 [transition_ transition:2 toView:[views_ lastObject]];
591 }
592
593 - (void) resetView {
594 resetting_ = true;
595 if ([[navbar_ navigationItems] count] == 1)
596 [self _resetView];
597 else do
598 [navbar_ popNavigationItem];
599 while ([[navbar_ navigationItems] count] != 1);
600 resetting_ = false;
601
602 [transition_ transition:0 toView:[views_ lastObject]];
603 }
604
605 - (void) _resetView {
606 [navbar_ showButtonsWithLeftTitle:nil rightTitle:nil];
607 }
608
609 - (void) setPrompt {
610 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
611 }
612
613 @end
614 /* }}} */
615 /* Confirmation View {{{ */
616 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
617 if ([packages count] == 0)
618 return;
619
620 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 110, false);
621 [fields setObject:text forKey:key];
622
623 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
624 CGColor blue(space, 0, 0, 0.4, 1);
625 [text setTextColor:blue];
626 CGColorSpaceRelease(space);
627 }
628
629 @protocol ConfirmationViewDelegate
630 - (void) cancel;
631 - (void) confirm;
632 @end
633
634 @interface ConfirmationView : UIView {
635 Database *database_;
636 id delegate_;
637 UITransitionView *transition_;
638 UIView *overlay_;
639 UINavigationBar *navbar_;
640 UIPreferencesTable *table_;
641 NSMutableDictionary *fields_;
642 UIAlertSheet *essential_;
643 }
644
645 - (void) dealloc;
646 - (void) cancel;
647
648 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
649 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
650 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
651
652 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
653 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
654 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
655 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
656 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
657
658 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
659
660 @end
661
662 @implementation ConfirmationView
663
664 - (void) dealloc {
665 [transition_ release];
666 [overlay_ release];
667 [navbar_ release];
668 [table_ release];
669 [fields_ release];
670 if (essential_ != nil)
671 [essential_ release];
672 [super dealloc];
673 }
674
675 - (void) cancel {
676 [transition_ transition:7 toView:nil];
677 [delegate_ cancel];
678 }
679
680 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
681 if (from != nil && to == nil)
682 [self removeFromSuperview];
683 }
684
685 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
686 switch (button) {
687 case 0:
688 if (essential_ != nil)
689 [essential_ popupAlertAnimated:YES];
690 else
691 [delegate_ confirm];
692 break;
693
694 case 1:
695 [self cancel];
696 break;
697 }
698 }
699
700 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
701 [essential_ dismiss];
702 [self cancel];
703 }
704
705 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
706 return 2;
707 }
708
709 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
710 switch (group) {
711 case 0: return @"Statistics";
712 case 1: return @"Modifications";
713
714 default: _assert(false);
715 }
716 }
717
718 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
719 switch (group) {
720 case 0: return 3;
721 case 1: return [fields_ count];
722
723 default: _assert(false);
724 }
725 }
726
727 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
728 if (group != 1 || row == -1)
729 return proposed;
730 else {
731 _assert(size_t(row) < [fields_ count]);
732 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
733 }
734 }
735
736 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
737 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
738 [cell setShowSelection:NO];
739
740 switch (group) {
741 case 0: switch (row) {
742 case 0: {
743 [cell setTitle:@"Downloading"];
744 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
745 } break;
746
747 case 1: {
748 [cell setTitle:@"Resuming At"];
749 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
750 } break;
751
752 case 2: {
753 double size([database_ cache]->UsrSize());
754
755 if (size < 0) {
756 [cell setTitle:@"Disk Freeing"];
757 [cell setValue:SizeString(-size)];
758 } else {
759 [cell setTitle:@"Disk Using"];
760 [cell setValue:SizeString(size)];
761 }
762 } break;
763
764 default: _assert(false);
765 } break;
766
767 case 1:
768 _assert(size_t(row) < [fields_ count]);
769 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
770 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
771 break;
772
773 default: _assert(false);
774 }
775
776 return cell;
777 }
778
779 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
780 if ((self = [super initWithFrame:[view bounds]]) != nil) {
781 database_ = database;
782 delegate_ = delegate;
783
784 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
785 [self addSubview:transition_];
786
787 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
788
789 CGSize navsize = [UINavigationBar defaultSize];
790 CGRect navrect = {{0, 0}, navsize};
791 CGRect bounds = [overlay_ bounds];
792
793 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
794 [navbar_ setBarStyle:1];
795 [navbar_ setDelegate:self];
796
797 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
798 [navbar_ pushNavigationItem:navitem];
799 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
800
801 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
802
803 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
804 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
805 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
806
807 bool essential(false);
808
809 pkgCacheFile &cache([database_ cache]);
810 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
811 NSString *name([NSString stringWithCString:iterator.Name()]);
812 if (cache[iterator].NewInstall())
813 [installing addObject:name];
814 else if (cache[iterator].Upgrade())
815 [upgrading addObject:name];
816 else if (cache[iterator].Delete()) {
817 [removing addObject:name];
818 if ((iterator->Flags & pkgCache::Flag::Essential) != 0)
819 essential = true;
820 }
821 }
822
823 if (!essential)
824 essential_ = nil;
825 else {
826 essential_ = [[UIAlertSheet alloc]
827 initWithTitle:@"Unable to Comply"
828 buttons:[NSArray arrayWithObjects:@"Okay", nil]
829 defaultButtonIndex:0
830 delegate:self
831 context:self
832 ];
833
834 [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."];
835 }
836
837 AddTextView(fields_, installing, @"Installing");
838 AddTextView(fields_, upgrading, @"Upgrading");
839 AddTextView(fields_, removing, @"Removing");
840
841 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
842 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
843 )];
844
845 [table_ setReusesTableCells:YES];
846 [table_ setDataSource:self];
847 [table_ reloadData];
848
849 [overlay_ addSubview:navbar_];
850 [overlay_ addSubview:table_];
851
852 [view addSubview:self];
853
854 [transition_ setDelegate:self];
855
856 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
857 [transition_ transition:0 toView:blank];
858 [transition_ transition:3 toView:overlay_];
859 } return self;
860 }
861
862 @end
863 /* }}} */
864
865 /* Package Class {{{ */
866 NSString *Scour(const char *field, const char *begin, const char *end) {
867 size_t i(0), l(strlen(field));
868
869 for (;;) {
870 const char *name = begin + i;
871 const char *colon = name + l;
872 const char *value = colon + 1;
873
874 if (
875 value < end &&
876 *colon == ':' &&
877 memcmp(name, field, l) == 0
878 ) {
879 while (value != end && value[0] == ' ')
880 ++value;
881 const char *line = std::find(value, end, '\n');
882 while (line != value && line[-1] == ' ')
883 --line;
884 return [NSString stringWithCString:value length:(line - value)];
885 } else {
886 begin = std::find(begin, end, '\n');
887 if (begin == end)
888 return nil;
889 ++begin;
890 }
891 }
892 }
893
894 @interface Package : NSObject {
895 pkgCache::PkgIterator iterator_;
896 Database *database_;
897 pkgCache::VerIterator version_;
898 pkgCache::VerFileIterator file_;
899
900 NSString *latest_;
901 NSString *installed_;
902
903 NSString *id_;
904 NSString *name_;
905 NSString *tagline_;
906 NSString *icon_;
907 NSString *bundle_;
908 NSString *website_;
909 }
910
911 - (void) dealloc;
912
913 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
914 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
915
916 - (NSString *) section;
917 - (Address *) maintainer;
918 - (size_t) size;
919 - (NSString *) description;
920 - (NSString *) index;
921
922 - (NSDate *) seen;
923
924 - (NSString *) latest;
925 - (NSString *) installed;
926 - (BOOL) upgradable;
927
928 - (NSString *) id;
929 - (NSString *) name;
930 - (NSString *) tagline;
931 - (NSString *) icon;
932 - (NSString *) bundle;
933 - (NSString *) website;
934
935 - (BOOL) matches:(NSString *)text;
936
937 - (NSComparisonResult) compareByName:(Package *)package;
938 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
939 - (NSComparisonResult) compareForChanges:(Package *)package;
940
941 - (void) install;
942 - (void) remove;
943 @end
944
945 @implementation Package
946
947 - (void) dealloc {
948 [latest_ release];
949 if (installed_ != nil)
950 [installed_ release];
951
952 [id_ release];
953 if (name_ != nil)
954 [name_ release];
955 [tagline_ release];
956 if (icon_ != nil)
957 [icon_ release];
958 if (bundle_ != nil)
959 [bundle_ release];
960 [super dealloc];
961 }
962
963 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
964 if ((self = [super init]) != nil) {
965 iterator_ = iterator;
966 database_ = database;
967
968 version_ = version;
969 file_ = file;
970
971 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
972
973 const char *begin, *end;
974 parser->GetRec(begin, end);
975
976 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
977 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
978
979 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
980 name_ = Scour("Name", begin, end);
981 if (name_ != nil)
982 name_ = [name_ retain];
983 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
984 icon_ = Scour("Icon", begin, end);
985 if (icon_ != nil)
986 icon_ = [icon_ retain];
987 bundle_ = Scour("Bundle", begin, end);
988 if (bundle_ != nil)
989 bundle_ = [bundle_ retain];
990 website_ = Scour("Website", begin, end);
991 if (website_ != nil)
992 website_ = [website_ retain];
993
994 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
995 if (metadata == nil) {
996 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
997 now_, @"FirstSeen",
998 nil];
999
1000 [Packages_ setObject:metadata forKey:id_];
1001 }
1002 } return self;
1003 }
1004
1005 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1006 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
1007 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
1008 return [[[Package alloc]
1009 initWithIterator:iterator
1010 database:database
1011 version:version
1012 file:file]
1013 autorelease];
1014 return nil;
1015 }
1016
1017 - (NSString *) section {
1018 return [[NSString stringWithCString:iterator_.Section()] stringByReplacingCharacter:'_' withCharacter:' '];
1019 }
1020
1021 - (Address *) maintainer {
1022 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1023 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
1024 }
1025
1026 - (size_t) size {
1027 return version_->InstalledSize;
1028 }
1029
1030 - (NSString *) description {
1031 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1032 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
1033
1034 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1035 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1036 if ([lines count] < 2)
1037 return nil;
1038
1039 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1040 for (size_t i(1); i != [lines count]; ++i) {
1041 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1042 [trimmed addObject:trim];
1043 }
1044
1045 return [trimmed componentsJoinedByString:@"\n"];
1046 }
1047
1048 - (NSString *) index {
1049 return [[[self name] substringToIndex:1] uppercaseString];
1050 }
1051
1052 - (NSDate *) seen {
1053 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1054 }
1055
1056 - (NSString *) latest {
1057 return latest_;
1058 }
1059
1060 - (NSString *) installed {
1061 return installed_;
1062 }
1063
1064 - (BOOL) upgradable {
1065 NSString *installed = [self installed];
1066 return installed != nil && [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1067 }
1068
1069 - (NSString *) id {
1070 return id_;
1071 }
1072
1073 - (NSString *) name {
1074 return name_ == nil ? id_ : name_;
1075 }
1076
1077 - (NSString *) tagline {
1078 return tagline_;
1079 }
1080
1081 - (NSString *) icon {
1082 return icon_;
1083 }
1084
1085 - (NSString *) bundle {
1086 return bundle_;
1087 }
1088
1089 - (NSString *) website {
1090 return website_;
1091 }
1092
1093 - (BOOL) matches:(NSString *)text {
1094 if (text == nil)
1095 return NO;
1096
1097 NSRange range;
1098
1099 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1100 if (range.location != NSNotFound)
1101 return YES;
1102
1103 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1104 if (range.location != NSNotFound)
1105 return YES;
1106
1107 return NO;
1108 }
1109
1110 - (NSComparisonResult) compareByName:(Package *)package {
1111 return [[self name] caseInsensitiveCompare:[package name]];
1112 }
1113
1114 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1115 NSComparisonResult result = [[self section] caseInsensitiveCompare:[package section]];
1116 if (result != NSOrderedSame)
1117 return result;
1118 return [self compareByName:package];
1119 }
1120
1121 - (NSComparisonResult) compareForChanges:(Package *)package {
1122 BOOL lhs = [self upgradable];
1123 BOOL rhs = [package upgradable];
1124
1125 if (lhs != rhs)
1126 return lhs ? NSOrderedAscending : NSOrderedDescending;
1127 else if (!lhs) {
1128 switch ([[self seen] compare:[package seen]]) {
1129 case NSOrderedAscending:
1130 return NSOrderedDescending;
1131 case NSOrderedSame:
1132 break;
1133 case NSOrderedDescending:
1134 return NSOrderedAscending;
1135 default:
1136 _assert(false);
1137 }
1138 }
1139
1140 return [self compareByName:package];
1141 }
1142
1143 - (void) install {
1144 pkgProblemResolver *resolver = [database_ resolver];
1145 resolver->Clear(iterator_);
1146 resolver->Protect(iterator_);
1147 [database_ cache]->MarkInstall(iterator_, false);
1148 }
1149
1150 - (void) remove {
1151 pkgProblemResolver *resolver = [database_ resolver];
1152 resolver->Clear(iterator_);
1153 resolver->Protect(iterator_);
1154 resolver->Remove(iterator_);
1155 [database_ cache]->MarkDelete(iterator_, true);
1156 }
1157
1158 @end
1159 /* }}} */
1160 /* Section Class {{{ */
1161 @interface Section : NSObject {
1162 NSString *name_;
1163 size_t row_;
1164 NSMutableArray *packages_;
1165 }
1166
1167 - (void) dealloc;
1168
1169 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1170 - (NSString *) name;
1171 - (size_t) row;
1172 - (NSArray *) packages;
1173 - (size_t) count;
1174 - (void) addPackage:(Package *)package;
1175 @end
1176
1177 @implementation Section
1178
1179 - (void) dealloc {
1180 [name_ release];
1181 [packages_ release];
1182 [super dealloc];
1183 }
1184
1185 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1186 if ((self = [super init]) != nil) {
1187 name_ = [name retain];
1188 row_ = row;
1189 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1190 } return self;
1191 }
1192
1193 - (NSString *) name {
1194 return name_;
1195 }
1196
1197 - (size_t) row {
1198 return row_;
1199 }
1200
1201 - (NSArray *) packages {
1202 return packages_;
1203 }
1204
1205 - (size_t) count {
1206 return [packages_ count];
1207 }
1208
1209 - (void) addPackage:(Package *)package {
1210 [packages_ addObject:package];
1211 }
1212
1213 @end
1214 /* }}} */
1215
1216 /* Package View {{{ */
1217 @protocol PackageViewDelegate
1218 - (void) performPackage:(Package *)package;
1219 @end
1220
1221 @interface PackageView : UIView {
1222 UIPreferencesTable *table_;
1223 Package *package_;
1224 UITextView *description_;
1225 id delegate_;
1226 }
1227
1228 - (void) dealloc;
1229
1230 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
1231 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
1232 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
1233 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
1234 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
1235
1236 - (BOOL) canSelectRow:(int)row;
1237 - (void) tableRowSelected:(NSNotification *)notification;
1238
1239 - (Package *) package;
1240
1241 - (id) initWithFrame:(struct CGRect)frame;
1242 - (void) setPackage:(Package *)package;
1243 - (void) setDelegate:(id)delegate;
1244 @end
1245
1246 @implementation PackageView
1247
1248 - (void) dealloc {
1249 if (package_ != nil)
1250 [package_ release];
1251 if (description_ != nil)
1252 [description_ release];
1253 [table_ release];
1254 [super dealloc];
1255 }
1256
1257 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
1258 return 2;
1259 }
1260
1261 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1262 switch (group) {
1263 case 0: return nil;
1264 case 1: return @"Details";
1265 case 2: return @"Source";
1266
1267 default: _assert(false);
1268 }
1269 }
1270
1271 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1272 if (group != 0 || row != 1)
1273 return proposed;
1274 else
1275 return [description_ visibleTextRect].size.height + TextViewOffset_;
1276 }
1277
1278 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1279 switch (group) {
1280 case 0: return 2;
1281 case 1: return 5;
1282 case 2: return 0;
1283
1284 default: _assert(false);
1285 }
1286 }
1287
1288 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
1289 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1290 [cell setShowSelection:NO];
1291
1292 switch (group) {
1293 case 0: switch (row) {
1294 case 0:
1295 [cell setTitle:[package_ name]];
1296 [cell setValue:[package_ latest]];
1297 break;
1298
1299 case 1:
1300 [cell addSubview:description_];
1301 break;
1302
1303 default: _assert(false);
1304 } break;
1305
1306 case 1: switch (row) {
1307 case 0:
1308 [cell setTitle:@"Identifier"];
1309 [cell setValue:[package_ id]];
1310 break;
1311
1312 case 1: {
1313 [cell setTitle:@"Installed Version"];
1314 NSString *installed([package_ installed]);
1315 [cell setValue:(installed == nil ? @"n/a" : installed)];
1316 } break;
1317
1318 case 2:
1319 [cell setTitle:@"Section"];
1320 [cell setValue:[package_ section]];
1321 break;
1322
1323 case 3:
1324 [cell setTitle:@"Expanded Size"];
1325 [cell setValue:SizeString([package_ size])];
1326 break;
1327
1328 case 4:
1329 [cell setTitle:@"Maintainer"];
1330 [cell setValue:[[package_ maintainer] name]];
1331 [cell setShowDisclosure:YES];
1332 [cell setShowSelection:YES];
1333 break;
1334
1335 default: _assert(false);
1336 } break;
1337
1338 case 2: switch (row) {
1339 } break;
1340
1341 default: _assert(false);
1342 }
1343
1344 return cell;
1345 }
1346
1347 - (BOOL) canSelectRow:(int)row {
1348 return YES;
1349 }
1350
1351 - (void) tableRowSelected:(NSNotification *)notification {
1352 printf("%d\n", [table_ selectedRow]);
1353 switch ([table_ selectedRow]) {
1354 case 8:
1355 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
1356 [[package_ maintainer] email],
1357 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
1358 ]]];
1359 break;
1360 }
1361 }
1362
1363 - (Package *) package {
1364 return package_;
1365 }
1366
1367 - (id) initWithFrame:(struct CGRect)frame {
1368 if ((self = [super initWithFrame:frame]) != nil) {
1369 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
1370 [self addSubview:table_];
1371
1372 [table_ setDataSource:self];
1373 [table_ setDelegate:self];
1374 } return self;
1375 }
1376
1377 - (void) setPackage:(Package *)package {
1378 if (package_ != nil) {
1379 [package_ autorelease];
1380 package_ = nil;
1381 }
1382
1383 if (description_ != nil) {
1384 [description_ release];
1385 description_ = nil;
1386 }
1387
1388 if (package != nil) {
1389 package_ = [package retain];
1390
1391 NSString *description([package description]);
1392 if (description == nil)
1393 description = [package tagline];
1394 description_ = [GetTextView(description, 12, true) retain];
1395
1396 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1397 CGColor black(space, 0, 0, 0, 1);
1398 [description_ setTextColor:black];
1399 CGColorSpaceRelease(space);
1400
1401 [table_ reloadData];
1402 }
1403 }
1404
1405 - (void) setDelegate:(id)delegate {
1406 delegate_ = delegate;
1407 }
1408
1409 @end
1410 /* }}} */
1411 /* Package Cell {{{ */
1412 @interface PackageCell : UITableCell {
1413 UITextLabel *name_;
1414 UITextLabel *version_;
1415 UITextLabel *description_;
1416 SEL versioner_;
1417 }
1418
1419 - (void) dealloc;
1420
1421 - (PackageCell *) initWithVersioner:(SEL)versioner;
1422 - (void) setPackage:(Package *)package;
1423
1424 - (void) _setSelected:(float)fraction;
1425 - (void) setSelected:(BOOL)selected;
1426 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
1427 - (void) _setSelectionFadeFraction:(float)fraction;
1428
1429 @end
1430
1431 @implementation PackageCell
1432
1433 - (void) dealloc {
1434 [name_ release];
1435 [version_ release];
1436 [description_ release];
1437 [super dealloc];
1438 }
1439
1440 - (PackageCell *) initWithVersioner:(SEL)versioner {
1441 if ((self = [super init]) != nil) {
1442 versioner_ = versioner;
1443
1444 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
1445 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
1446 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
1447
1448 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1449
1450 CGColor clear(space, 0, 0, 0, 0);
1451
1452 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
1453 [name_ setBackgroundColor:clear];
1454 [name_ setFont:bold];
1455
1456 version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(286, 7, 70, 25)];
1457 [version_ setBackgroundColor:clear];
1458 [version_ setFont:large];
1459
1460 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
1461 [description_ setBackgroundColor:clear];
1462 [description_ setFont:small];
1463
1464 [self addSubview:name_];
1465 [self addSubview:version_];
1466 [self addSubview:description_];
1467
1468 CGColorSpaceRelease(space);
1469
1470 CFRelease(small);
1471 CFRelease(large);
1472 CFRelease(bold);
1473 } return self;
1474 }
1475
1476 - (void) setPackage:(Package *)package {
1477 [name_ setText:[package name]];
1478 [version_ setText:[package latest]];
1479 [description_ setText:[package tagline]];
1480 }
1481
1482 - (void) _setSelected:(float)fraction {
1483 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1484
1485 CGColor black(space,
1486 interpolate(0.0, 1.0, fraction),
1487 interpolate(0.0, 1.0, fraction),
1488 interpolate(0.0, 1.0, fraction),
1489 1.0);
1490
1491 CGColor blue(space,
1492 interpolate(0.2, 1.0, fraction),
1493 interpolate(0.2, 1.0, fraction),
1494 interpolate(1.0, 1.0, fraction),
1495 1.0);
1496
1497 CGColor gray(space,
1498 interpolate(0.4, 1.0, fraction),
1499 interpolate(0.4, 1.0, fraction),
1500 interpolate(0.4, 1.0, fraction),
1501 1.0);
1502
1503 [name_ setColor:black];
1504 [version_ setColor:blue];
1505 [description_ setColor:gray];
1506
1507 CGColorSpaceRelease(space);
1508 }
1509
1510 - (void) setSelected:(BOOL)selected {
1511 [self _setSelected:(selected ? 1.0 : 0.0)];
1512 [super setSelected:selected];
1513 }
1514
1515 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
1516 if (!fade)
1517 [self _setSelected:(selected ? 1.0 : 0.0)];
1518 [super setSelected:selected withFade:fade];
1519 }
1520
1521 - (void) _setSelectionFadeFraction:(float)fraction {
1522 [self _setSelected:fraction];
1523 [super _setSelectionFadeFraction:fraction];
1524 }
1525
1526 @end
1527 /* }}} */
1528
1529 /* Database Implementation {{{ */
1530 @implementation Database
1531
1532 - (void) dealloc {
1533 _assert(false);
1534 [super dealloc];
1535 }
1536
1537 - (void) _readStatus:(NSNumber *)fd {
1538 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1539
1540 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1541 std::istream is(&ib);
1542 std::string line;
1543
1544 const char *error;
1545 int offset;
1546 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1547
1548 pcre_extra *study = NULL;
1549 int capture;
1550 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1551 int matches[(capture + 1) * 3];
1552
1553 while (std::getline(is, line)) {
1554 const char *data(line.c_str());
1555
1556 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1557
1558 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1559 float percent;
1560 buffer >> percent;
1561 [delegate_ setPercent:(percent / 100)];
1562
1563 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1564 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1565
1566 if (type == "pmerror")
1567 [delegate_ setError:string];
1568 else if (type == "pmstatus")
1569 [delegate_ setTitle:string];
1570 else if (type == "pmconffile")
1571 ;
1572 else _assert(false);
1573 }
1574
1575 [pool release];
1576 _assert(false);
1577 }
1578
1579 - (void) _readOutput:(NSNumber *)fd {
1580 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1581
1582 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1583 std::istream is(&ib);
1584 std::string line;
1585
1586 while (std::getline(is, line))
1587 [delegate_ addOutput:[NSString stringWithCString:line.c_str()]];
1588
1589 [pool release];
1590 _assert(false);
1591 }
1592
1593 - (Package *) packageWithName:(NSString *)name {
1594 pkgCache::PkgIterator iterator(cache_->FindPkg([name cString]));
1595 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1596 }
1597
1598 - (Database *) init {
1599 if ((self = [super init]) != nil) {
1600 records_ = NULL;
1601 resolver_ = NULL;
1602 fetcher_ = NULL;
1603 lock_ = NULL;
1604
1605 int fds[2];
1606
1607 _assert(pipe(fds) != -1);
1608 statusfd_ = fds[1];
1609
1610 [NSThread
1611 detachNewThreadSelector:@selector(_readStatus:)
1612 toTarget:self
1613 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1614 ];
1615
1616 _assert(pipe(fds) != -1);
1617 _assert(dup2(fds[1], 1) != -1);
1618 _assert(close(fds[1]) != -1);
1619
1620 [NSThread
1621 detachNewThreadSelector:@selector(_readOutput:)
1622 toTarget:self
1623 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1624 ];
1625 } return self;
1626 }
1627
1628 - (pkgCacheFile &) cache {
1629 return cache_;
1630 }
1631
1632 - (pkgRecords *) records {
1633 return records_;
1634 }
1635
1636 - (pkgProblemResolver *) resolver {
1637 return resolver_;
1638 }
1639
1640 - (pkgAcquire &) fetcher {
1641 return *fetcher_;
1642 }
1643
1644 - (void) reloadData {
1645 _error->Discard();
1646 manager_ = NULL;
1647 delete lock_;
1648 delete fetcher_;
1649 delete resolver_;
1650 delete records_;
1651 cache_.Close();
1652 _assert(cache_.Open(progress_, true));
1653 records_ = new pkgRecords(cache_);
1654 resolver_ = new pkgProblemResolver(cache_);
1655 fetcher_ = new pkgAcquire(&status_);
1656 lock_ = NULL;
1657 }
1658
1659 - (void) prepare {
1660 pkgRecords records(cache_);
1661
1662 lock_ = new FileFd();
1663 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1664 _assert(!_error->PendingError());
1665
1666 pkgSourceList list;
1667 _assert(list.ReadMainList());
1668
1669 manager_ = (_system->CreatePM(cache_));
1670 _assert(manager_->GetArchives(fetcher_, &list, &records));
1671 _assert(!_error->PendingError());
1672 }
1673
1674 - (void) perform {
1675 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1676 return;
1677
1678 _system->UnLock();
1679 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1680
1681 if (result == pkgPackageManager::Failed)
1682 return;
1683 if (_error->PendingError())
1684 return;
1685 if (result != pkgPackageManager::Completed)
1686 return;
1687 }
1688
1689 - (void) update {
1690 pkgSourceList list;
1691 _assert(list.ReadMainList());
1692
1693 FileFd lock;
1694 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1695 _assert(!_error->PendingError());
1696
1697 pkgAcquire fetcher(&status_);
1698 _assert(list.GetIndexes(&fetcher));
1699 _assert(fetcher.Run(PulseInterval_) != pkgAcquire::Failed);
1700
1701 bool failed = false;
1702 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1703 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1704 (*item)->Finished();
1705 failed = true;
1706 }
1707
1708 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1709 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1710 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1711 }
1712
1713 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1714 }
1715
1716 - (void) upgrade {
1717 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1718 _assert(pkgApplyStatus(cache_));
1719
1720 if (cache_->BrokenCount() != 0) {
1721 _assert(pkgFixBroken(cache_));
1722 _assert(cache_->BrokenCount() == 0);
1723 _assert(pkgMinimizeUpgrade(cache_));
1724 }
1725
1726 _assert(pkgDistUpgrade(cache_));
1727 }
1728
1729 - (void) setDelegate:(id)delegate {
1730 delegate_ = delegate;
1731 status_.setDelegate(delegate);
1732 progress_.setDelegate(delegate);
1733 }
1734
1735 @end
1736 /* }}} */
1737
1738 /* Progress Data {{{ */
1739 @interface ProgressData : NSObject {
1740 SEL selector_;
1741 id target_;
1742 id object_;
1743 }
1744
1745 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
1746
1747 - (SEL) selector;
1748 - (id) target;
1749 - (id) object;
1750 @end
1751
1752 @implementation ProgressData
1753
1754 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
1755 if ((self = [super init]) != nil) {
1756 selector_ = selector;
1757 target_ = target;
1758 object_ = object;
1759 } return self;
1760 }
1761
1762 - (SEL) selector {
1763 return selector_;
1764 }
1765
1766 - (id) target {
1767 return target_;
1768 }
1769
1770 - (id) object {
1771 return object_;
1772 }
1773
1774 @end
1775 /* }}} */
1776 /* Progress View {{{ */
1777 @interface ProgressView : UIView <
1778 ProgressDelegate
1779 > {
1780 UIView *view_;
1781 UIView *background_;
1782 UITransitionView *transition_;
1783 UIView *overlay_;
1784 UINavigationBar *navbar_;
1785 UIProgressBar *progress_;
1786 UITextView *output_;
1787 UITextLabel *status_;
1788 id delegate_;
1789 }
1790
1791 - (void) dealloc;
1792
1793 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
1794
1795 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
1796 - (void) setContentView:(UIView *)view;
1797 - (void) resetView;
1798
1799 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
1800
1801 - (void) _retachThread;
1802 - (void) _detachNewThreadData:(ProgressData *)data;
1803 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
1804
1805 - (void) setError:(NSString *)error;
1806 - (void) _setError:(NSString *)error;
1807
1808 - (void) setTitle:(NSString *)title;
1809 - (void) _setTitle:(NSString *)title;
1810
1811 - (void) setPercent:(float)percent;
1812 - (void) _setPercent:(NSNumber *)percent;
1813
1814 - (void) addOutput:(NSString *)output;
1815 - (void) _addOutput:(NSString *)output;
1816 @end
1817
1818 @protocol ProgressViewDelegate
1819 - (void) progressViewIsComplete:(ProgressView *)sender;
1820 @end
1821
1822 @implementation ProgressView
1823
1824 - (void) dealloc {
1825 [view_ release];
1826 if (background_ != nil)
1827 [background_ release];
1828 [transition_ release];
1829 [overlay_ release];
1830 [navbar_ release];
1831 [progress_ release];
1832 [output_ release];
1833 [status_ release];
1834 [super dealloc];
1835 }
1836
1837 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1838 if (bootstrap_ && from == overlay_ && to == view_)
1839 exit(0);
1840 }
1841
1842 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
1843 if ((self = [super initWithFrame:frame]) != nil) {
1844 delegate_ = delegate;
1845
1846 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1847
1848 CGColor black(space, 0.0, 0.0, 0.0, 1.0);
1849 CGColor white(space, 1.0, 1.0, 1.0, 1.0);
1850 CGColor clear(space, 0.0, 0.0, 0.0, 0.0);
1851
1852 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1853 [transition_ setDelegate:self];
1854
1855 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1856
1857 if (bootstrap_)
1858 [overlay_ setBackgroundColor:black];
1859 else {
1860 background_ = [[UIView alloc] initWithFrame:[self bounds]];
1861 [background_ setBackgroundColor:black];
1862 [self addSubview:background_];
1863 }
1864
1865 [self addSubview:transition_];
1866
1867 CGSize navsize = [UINavigationBar defaultSize];
1868 CGRect navrect = {{0, 0}, navsize};
1869
1870 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1871 [overlay_ addSubview:navbar_];
1872
1873 [navbar_ setBarStyle:1];
1874 [navbar_ setDelegate:self];
1875
1876 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
1877 [navbar_ pushNavigationItem:navitem];
1878
1879 CGRect bounds = [overlay_ bounds];
1880 CGSize prgsize = [UIProgressBar defaultSize];
1881
1882 CGRect prgrect = {{
1883 (bounds.size.width - prgsize.width) / 2,
1884 bounds.size.height - prgsize.height - 20
1885 }, prgsize};
1886
1887 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
1888 [overlay_ addSubview:progress_];
1889
1890 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
1891 10,
1892 bounds.size.height - prgsize.height - 50,
1893 bounds.size.width - 20,
1894 24
1895 )];
1896
1897 [status_ setColor:white];
1898 [status_ setBackgroundColor:clear];
1899
1900 [status_ setCentersHorizontally:YES];
1901 //[status_ setFont:font];
1902
1903 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
1904 10,
1905 navrect.size.height + 20,
1906 bounds.size.width - 20,
1907 bounds.size.height - navsize.height - 62 - navrect.size.height
1908 )];
1909
1910 //[output_ setTextFont:@"Courier New"];
1911 [output_ setTextSize:12];
1912
1913 [output_ setTextColor:white];
1914 [output_ setBackgroundColor:clear];
1915
1916 [output_ setMarginTop:0];
1917 [output_ setAllowsRubberBanding:YES];
1918
1919 [overlay_ addSubview:output_];
1920 [overlay_ addSubview:status_];
1921
1922 [progress_ setStyle:0];
1923
1924 CGColorSpaceRelease(space);
1925 } return self;
1926 }
1927
1928 - (void) setContentView:(UIView *)view {
1929 view_ = [view retain];
1930 }
1931
1932 - (void) resetView {
1933 [transition_ transition:6 toView:view_];
1934 }
1935
1936 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1937 [sheet dismiss];
1938 }
1939
1940 - (void) _retachThread {
1941 [delegate_ progressViewIsComplete:self];
1942 [self resetView];
1943 }
1944
1945 - (void) _detachNewThreadData:(ProgressData *)data {
1946 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1947
1948 [[data target] performSelector:[data selector] withObject:[data object]];
1949 [data release];
1950
1951 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
1952
1953 [pool release];
1954 }
1955
1956 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
1957 [navbar_ popNavigationItem];
1958 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1959 [navbar_ pushNavigationItem:navitem];
1960
1961 [status_ setText:nil];
1962 [output_ setText:@""];
1963 [progress_ setProgress:0];
1964
1965 [transition_ transition:6 toView:overlay_];
1966
1967 [NSThread
1968 detachNewThreadSelector:@selector(_detachNewThreadData:)
1969 toTarget:self
1970 withObject:[[ProgressData alloc]
1971 initWithSelector:selector
1972 target:target
1973 object:object
1974 ]
1975 ];
1976 }
1977
1978 - (void) setError:(NSString *)error {
1979 [self
1980 performSelectorOnMainThread:@selector(_setError:)
1981 withObject:error
1982 waitUntilDone:YES
1983 ];
1984 }
1985
1986 - (void) _setError:(NSString *)error {
1987 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
1988 initWithTitle:@"Package Error"
1989 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1990 defaultButtonIndex:0
1991 delegate:self
1992 context:self
1993 ] autorelease];
1994
1995 [sheet setBodyText:error];
1996 [sheet popupAlertAnimated:YES];
1997 }
1998
1999 - (void) setTitle:(NSString *)title {
2000 [self
2001 performSelectorOnMainThread:@selector(_setTitle:)
2002 withObject:title
2003 waitUntilDone:YES
2004 ];
2005 }
2006
2007 - (void) _setTitle:(NSString *)title {
2008 [status_ setText:[title stringByAppendingString:@"..."]];
2009 }
2010
2011 - (void) setPercent:(float)percent {
2012 [self
2013 performSelectorOnMainThread:@selector(_setPercent:)
2014 withObject:[NSNumber numberWithFloat:percent]
2015 waitUntilDone:YES
2016 ];
2017 }
2018
2019 - (void) _setPercent:(NSNumber *)percent {
2020 [progress_ setProgress:[percent floatValue]];
2021 }
2022
2023 - (void) addOutput:(NSString *)output {
2024 [self
2025 performSelectorOnMainThread:@selector(_addOutput:)
2026 withObject:output
2027 waitUntilDone:YES
2028 ];
2029 }
2030
2031 - (void) _addOutput:(NSString *)output {
2032 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2033 CGSize size = [output_ contentSize];
2034 CGRect rect = {{0, size.height}, {size.width, 0}};
2035 [output_ scrollRectToVisible:rect animated:YES];
2036 }
2037
2038 @end
2039 /* }}} */
2040
2041 /* Package Table {{{ */
2042 @protocol PackageTableDelegate
2043 - (void) packageTable:(id)table packageSelected:(Package *)package;
2044 @end
2045
2046 @interface PackageTable : UIView {
2047 SEL versioner_;
2048 UISectionList *list_;
2049
2050 id delegate_;
2051 NSArray *packages_;
2052 NSMutableArray *sections_;
2053 }
2054
2055 - (void) dealloc;
2056
2057 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
2058 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
2059 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
2060
2061 - (int) numberOfRowsInTable:(UITable *)table;
2062 - (float) table:(UITable *)table heightForRow:(int)row;
2063 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2064 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2065 - (void) tableRowSelected:(NSNotification *)notification;
2066
2067 - (id) initWithFrame:(CGRect)frame versioner:(SEL)versioner;
2068
2069 - (void) setDelegate:(id)delegate;
2070 - (void) setPackages:(NSArray *)packages;
2071
2072 - (void) resetViewAnimated:(BOOL)animated;
2073 - (UITable *) table;
2074 @end
2075
2076 @implementation PackageTable
2077
2078 - (void) dealloc {
2079 [list_ release];
2080 [sections_ release];
2081 if (packages_ != nil)
2082 [packages_ release];
2083 [super dealloc];
2084 }
2085
2086 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2087 return [sections_ count];
2088 }
2089
2090 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2091 return [[sections_ objectAtIndex:section] name];
2092 }
2093
2094 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2095 return [[sections_ objectAtIndex:section] row];
2096 }
2097
2098 - (int) numberOfRowsInTable:(UITable *)table {
2099 return [packages_ count];
2100 }
2101
2102 - (float) table:(UITable *)table heightForRow:(int)row {
2103 return 64;
2104 }
2105
2106 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2107 if (reusing == nil)
2108 reusing = [[[PackageCell alloc] initWithVersioner:versioner_] autorelease];
2109 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2110 return reusing;
2111 }
2112
2113 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2114 return NO;
2115 }
2116
2117 - (void) tableRowSelected:(NSNotification *)notification {
2118 int row = [[notification object] selectedRow];
2119 [delegate_ packageTable:self packageSelected:(row == INT_MAX ? nil : [packages_ objectAtIndex:row])];
2120 }
2121
2122 - (id) initWithFrame:(CGRect)frame versioner:(SEL)versioner {
2123 if ((self = [super initWithFrame:frame]) != nil) {
2124 versioner_ = versioner;
2125 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2126
2127 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2128 [list_ setDataSource:self];
2129
2130 UITableColumn *column = [[[UITableColumn alloc]
2131 initWithTitle:@"Name"
2132 identifier:@"name"
2133 width:frame.size.width
2134 ] autorelease];
2135
2136 UITable *table = [list_ table];
2137 [table setSeparatorStyle:1];
2138 [table addTableColumn:column];
2139 [table setDelegate:self];
2140 [table setReusesTableCells:YES];
2141
2142 [self addSubview:list_];
2143 } return self;
2144 }
2145
2146 - (void) setDelegate:(id)delegate {
2147 delegate_ = delegate;
2148 }
2149
2150 - (void) setPackages:(NSArray *)packages {
2151 if (packages_ != nil)
2152 [packages_ autorelease];
2153 _assert(packages != nil);
2154 packages_ = [packages retain];
2155
2156 [sections_ removeAllObjects];
2157
2158 Section *section = nil;
2159
2160 for (size_t offset(0); offset != [packages_ count]; ++offset) {
2161 Package *package = [packages_ objectAtIndex:offset];
2162 NSString *name = [package index];
2163
2164 if (section == nil || ![[section name] isEqual:name]) {
2165 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2166 [sections_ addObject:section];
2167 }
2168
2169 [section addPackage:package];
2170 }
2171
2172 [list_ reloadData];
2173 }
2174
2175 - (void) resetViewAnimated:(BOOL)animated {
2176 [[list_ table] selectRow:-1 byExtendingSelection:NO withFade:animated];
2177 }
2178
2179 - (UITable *) table {
2180 return [list_ table];
2181 }
2182
2183 @end
2184 /* }}} */
2185
2186 /* Section Cell {{{ */
2187 @interface SectionCell : UITableCell {
2188 UITextLabel *name_;
2189 UITextLabel *count_;
2190 }
2191
2192 - (void) dealloc;
2193
2194 - (id) init;
2195 - (void) setSection:(Section *)section;
2196
2197 - (void) _setSelected:(float)fraction;
2198 - (void) setSelected:(BOOL)selected;
2199 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2200 - (void) _setSelectionFadeFraction:(float)fraction;
2201
2202 @end
2203
2204 @implementation SectionCell
2205
2206 - (void) dealloc {
2207 [name_ release];
2208 [count_ release];
2209 [super dealloc];
2210 }
2211
2212 - (id) init {
2213 if ((self = [super init]) != nil) {
2214 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2215 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2216
2217 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2218 CGColor clear(space, 0, 0, 0, 0);
2219 CGColor white(space, 1, 1, 1, 1);
2220
2221 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(47, 9, 250, 25)];
2222 [name_ setBackgroundColor:clear];
2223 [name_ setFont:bold];
2224
2225 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2226 [count_ setCentersHorizontally:YES];
2227 [count_ setBackgroundColor:clear];
2228 [count_ setFont:small];
2229 [count_ setColor:white];
2230
2231 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2232 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2233
2234 [self addSubview:folder];
2235 [self addSubview:name_];
2236 [self addSubview:count_];
2237
2238 [self _setSelected:0];
2239
2240 CGColorSpaceRelease(space);
2241
2242 CFRelease(small);
2243 CFRelease(bold);
2244 } return self;
2245 }
2246
2247 - (void) setSection:(Section *)section {
2248 if (section == nil) {
2249 [name_ setText:@"All Packages"];
2250 [count_ setText:nil];
2251 } else {
2252 [name_ setText:[section name]];
2253 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2254 }
2255 }
2256
2257 - (void) _setSelected:(float)fraction {
2258 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2259
2260 CGColor black(space,
2261 interpolate(0.0, 1.0, fraction),
2262 interpolate(0.0, 1.0, fraction),
2263 interpolate(0.0, 1.0, fraction),
2264 1.0);
2265
2266 [name_ setColor:black];
2267
2268 CGColorSpaceRelease(space);
2269 }
2270
2271 - (void) setSelected:(BOOL)selected {
2272 [self _setSelected:(selected ? 1.0 : 0.0)];
2273 [super setSelected:selected];
2274 }
2275
2276 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2277 if (!fade)
2278 [self _setSelected:(selected ? 1.0 : 0.0)];
2279 [super setSelected:selected withFade:fade];
2280 }
2281
2282 - (void) _setSelectionFadeFraction:(float)fraction {
2283 [self _setSelected:fraction];
2284 [super _setSelectionFadeFraction:fraction];
2285 }
2286
2287 @end
2288 /* }}} */
2289 /* Install View {{{ */
2290 @interface InstallView : ResetView <
2291 PackageTableDelegate
2292 > {
2293 NSArray *sections_;
2294 UITable *list_;
2295 PackageTable *table_;
2296 PackageView *view_;
2297 NSString *section_;
2298 NSString *package_;
2299 NSMutableArray *packages_;
2300 }
2301
2302 - (void) dealloc;
2303
2304 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2305
2306 - (int) numberOfRowsInTable:(UITable *)table;
2307 - (float) table:(UITable *)table heightForRow:(int)row;
2308 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2309 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2310 - (void) tableRowSelected:(NSNotification *)notification;
2311
2312 - (void) packageTable:(id)table packageSelected:(Package *)package;
2313
2314 - (id) initWithFrame:(CGRect)frame;
2315 - (void) setPackages:(NSArray *)packages;
2316 - (void) setDelegate:(id)delegate;
2317 @end
2318
2319 @implementation InstallView
2320
2321 - (void) dealloc {
2322 [packages_ release];
2323 if (sections_ != nil)
2324 [sections_ release];
2325 if (list_ != nil)
2326 [list_ release];
2327 if (table_ != nil)
2328 [table_ release];
2329 if (view_ != nil)
2330 [view_ release];
2331 if (section_ != nil)
2332 [section_ release];
2333 if (package_ != nil)
2334 [package_ release];
2335 [super dealloc];
2336 }
2337
2338 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2339 if (button == 0) {
2340 [[view_ package] install];
2341 [delegate_ resolve];
2342 [delegate_ perform];
2343 }
2344 }
2345
2346 - (int) numberOfRowsInTable:(UITable *)table {
2347 return [sections_ count] + 1;
2348 }
2349
2350 - (float) table:(UITable *)table heightForRow:(int)row {
2351 return 45;
2352 }
2353
2354 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2355 if (reusing == nil)
2356 reusing = [[[SectionCell alloc] init] autorelease];
2357 if (row == 0)
2358 [(SectionCell *)reusing setSection:nil];
2359 else
2360 [(SectionCell *)reusing setSection:[sections_ objectAtIndex:(row - 1)]];
2361 return reusing;
2362 }
2363
2364 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2365 return YES;
2366 }
2367
2368 - (void) tableRowSelected:(NSNotification *)notification {
2369 int row = [[notification object] selectedRow];
2370
2371 if (row == INT_MAX) {
2372 [section_ release];
2373 section_ = nil;
2374
2375 [table_ release];
2376 table_ = nil;
2377 } else {
2378 _assert(section_ == nil);
2379 _assert(table_ == nil);
2380
2381 Section *section;
2382 NSString *name;
2383
2384 if (row == 0) {
2385 section = nil;
2386 section_ = nil;
2387 name = @"All Packages";
2388 } else {
2389 section = [sections_ objectAtIndex:(row - 1)];
2390 name = [section name];
2391 section_ = [name retain];
2392 }
2393
2394 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2395 [table_ setDelegate:self];
2396 [table_ setPackages:(section == nil ? packages_ : [section packages])];
2397
2398 [self pushView:table_ withTitle:name backButtonTitle:@"Packages" rightButton:nil];
2399 }
2400 }
2401
2402 - (void) packageTable:(id)table packageSelected:(Package *)package {
2403 if (package == nil) {
2404 [package_ release];
2405 package_ = nil;
2406
2407 [view_ release];
2408 view_ = nil;
2409 } else {
2410 _assert(package_ == nil);
2411 _assert(view_ == nil);
2412
2413 package_ = [[package name] retain];
2414
2415 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2416 [view_ setDelegate:delegate_];
2417
2418 [view_ setPackage:package];
2419
2420 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Install"];
2421 }
2422 }
2423
2424 - (id) initWithFrame:(CGRect)frame {
2425 if ((self = [super initWithFrame:frame]) != nil) {
2426 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2427
2428 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
2429 [self pushView:list_ withTitle:@"Install" backButtonTitle:@"Sections" rightButton:nil];
2430
2431 UITableColumn *column = [[[UITableColumn alloc]
2432 initWithTitle:@"Name"
2433 identifier:@"name"
2434 width:frame.size.width
2435 ] autorelease];
2436
2437 [list_ setDataSource:self];
2438 [list_ setSeparatorStyle:1];
2439 [list_ addTableColumn:column];
2440 [list_ setDelegate:self];
2441 [list_ setReusesTableCells:YES];
2442
2443 [transition_ transition:0 toView:list_];
2444 } return self;
2445 }
2446
2447 - (void) setPackages:(NSArray *)packages {
2448 [packages_ removeAllObjects];
2449
2450 for (size_t i(0); i != [packages count]; ++i) {
2451 Package *package([packages objectAtIndex:i]);
2452 if ([package installed] == nil)
2453 [packages_ addObject:package];
2454 }
2455
2456 [packages_ sortUsingSelector:@selector(compareBySectionAndName:)];
2457 NSMutableArray *sections = [NSMutableArray arrayWithCapacity:16];
2458
2459 Section *nsection = nil;
2460 Package *npackage = nil;
2461
2462 Section *section = nil;
2463 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2464 Package *package = [packages_ objectAtIndex:offset];
2465 NSString *name = [package section];
2466
2467 if (section == nil || ![[section name] isEqual:name]) {
2468 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2469
2470 if ([name isEqualToString:section_])
2471 nsection = section;
2472 [sections addObject:section];
2473 }
2474
2475 if ([[package name] isEqualToString:package_])
2476 npackage = package;
2477 [section addPackage:package];
2478 }
2479
2480 if (sections_ != nil)
2481 [sections_ release];
2482 sections_ = [sections retain];
2483
2484 [packages_ sortUsingSelector:@selector(compareByName:)];
2485
2486 [list_ reloadData];
2487
2488 unsigned views(0);
2489
2490 if (npackage != nil)
2491 [view_ setPackage:npackage];
2492 else if (package_ != nil)
2493 ++views;
2494
2495 if (nsection != nil)
2496 [table_ setPackages:[nsection packages]];
2497 else if (section_ != nil)
2498 ++views;
2499
2500 [self popViews:views];
2501 [self setPrompt];
2502 }
2503
2504 - (void) setDelegate:(id)delegate {
2505 if (view_ != nil)
2506 [view_ setDelegate:delegate];
2507 [super setDelegate:delegate];
2508 }
2509
2510 @end
2511 /* }}} */
2512 /* Changes View {{{ */
2513 @interface ChangesView : ResetView <
2514 PackageTableDelegate
2515 > {
2516 UISectionList *list_;
2517 NSMutableArray *packages_;
2518 NSMutableArray *sections_;
2519 PackageView *view_;
2520 NSString *package_;
2521 size_t count_;
2522 }
2523
2524 - (void) dealloc;
2525
2526 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2527
2528 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
2529 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
2530 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
2531
2532 - (int) numberOfRowsInTable:(UITable *)table;
2533 - (float) table:(UITable *)table heightForRow:(int)row;
2534 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing;
2535 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
2536 - (void) tableRowSelected:(NSNotification *)notification;
2537
2538 - (id) initWithFrame:(CGRect)frame;
2539 - (void) setPackages:(NSArray *)packages;
2540 - (void) _resetView;
2541 - (size_t) count;
2542
2543 - (void) setDelegate:(id)delegate;
2544 @end
2545
2546 @implementation ChangesView
2547
2548 - (void) dealloc {
2549 [list_ release];
2550 [packages_ release];
2551 [sections_ release];
2552 if (view_ != nil)
2553 [view_ release];
2554 if (package_ != nil)
2555 [package_ release];
2556 [super dealloc];
2557 }
2558
2559 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2560 return [sections_ count];
2561 }
2562
2563 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2564 return [[sections_ objectAtIndex:section] name];
2565 }
2566
2567 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2568 return [[sections_ objectAtIndex:section] row];
2569 }
2570
2571 - (int) numberOfRowsInTable:(UITable *)table {
2572 return [packages_ count];
2573 }
2574
2575 - (float) table:(UITable *)table heightForRow:(int)row {
2576 return 64;
2577 }
2578
2579 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2580 if (reusing == nil)
2581 reusing = [[[PackageCell alloc] initWithVersioner:NULL] autorelease];
2582 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2583 return reusing;
2584 }
2585
2586 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2587 return NO;
2588 }
2589
2590 - (void) tableRowSelected:(NSNotification *)notification {
2591 int row = [[notification object] selectedRow];
2592 [self packageTable:self packageSelected:(row == INT_MAX ? nil : [packages_ objectAtIndex:row])];
2593 }
2594
2595 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2596 switch (button) {
2597 case 0:
2598 [[view_ package] install];
2599 [delegate_ resolve];
2600 [delegate_ perform];
2601 break;
2602
2603 case 1:
2604 [delegate_ upgrade];
2605 break;
2606 }
2607 }
2608
2609 - (void) packageTable:(id)table packageSelected:(Package *)package {
2610 if (package == nil) {
2611 [package_ release];
2612 package_ = nil;
2613
2614 [view_ release];
2615 view_ = nil;
2616 } else {
2617 _assert(package_ == nil);
2618 _assert(view_ == nil);
2619
2620 package_ = [[package name] retain];
2621
2622 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2623 [view_ setDelegate:delegate_];
2624
2625 [view_ setPackage:package];
2626
2627 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:(
2628 [package upgradable] ? @"Upgrade" : @"Install"
2629 )];
2630 }
2631 }
2632
2633 - (id) initWithFrame:(CGRect)frame {
2634 if ((self = [super initWithFrame:frame]) != nil) {
2635 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2636 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2637
2638 list_ = [[UISectionList alloc] initWithFrame:[transition_ bounds] showSectionIndex:NO];
2639 [list_ setShouldHideHeaderInShortLists:NO];
2640 [list_ setDataSource:self];
2641 //[list_ setSectionListStyle:1];
2642
2643 UITableColumn *column = [[[UITableColumn alloc]
2644 initWithTitle:@"Name"
2645 identifier:@"name"
2646 width:frame.size.width
2647 ] autorelease];
2648
2649 UITable *table = [list_ table];
2650 [table setSeparatorStyle:1];
2651 [table addTableColumn:column];
2652 [table setDelegate:self];
2653 [table setReusesTableCells:YES];
2654
2655 [self pushView:list_ withTitle:@"Changes" backButtonTitle:nil rightButton:nil];
2656 } return self;
2657 }
2658
2659 - (void) setPackages:(NSArray *)packages {
2660 [packages_ removeAllObjects];
2661 for (size_t i(0); i != [packages count]; ++i) {
2662 Package *package([packages objectAtIndex:i]);
2663 if ([package installed] == nil || [package upgradable])
2664 [packages_ addObject:package];
2665 }
2666
2667 [packages_ sortUsingSelector:@selector(compareForChanges:)];
2668
2669 [sections_ removeAllObjects];
2670
2671 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
2672 Section *section = nil;
2673
2674 count_ = 0;
2675 Package *npackage = nil;
2676 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2677 Package *package = [packages_ objectAtIndex:offset];
2678 if ([[package name] isEqualToString:package_])
2679 npackage = package;
2680
2681 if ([package upgradable])
2682 [upgradable addPackage:package];
2683 else {
2684 NSDate *seen = [package seen];
2685
2686 CFLocaleRef locale = CFLocaleCopyCurrent();
2687 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
2688 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
2689
2690 NSString *name = (NSString *) formatted;
2691
2692 if (section == nil || ![[section name] isEqual:name]) {
2693 section = [[[Section alloc] initWithName:name row:offset] autorelease];
2694 [sections_ addObject:section];
2695 }
2696
2697 [section addPackage:package];
2698
2699 CFRelease(formatter);
2700 CFRelease(formatted);
2701 CFRelease(locale);
2702 }
2703 }
2704
2705 count_ = [[upgradable packages] count];
2706 if (count_ != 0)
2707 [sections_ insertObject:upgradable atIndex:0];
2708
2709 [list_ reloadData];
2710
2711 if (npackage != nil)
2712 [view_ setPackage:npackage];
2713 else if (package_ != nil)
2714 [self popViews:1];
2715 if ([views_ count] == 1)
2716 [self _resetView];
2717
2718 [self setPrompt];
2719 }
2720
2721 - (void) _resetView {
2722 [navbar_ showButtonsWithLeftTitle:(count_ == 0 ? nil : @"Upgrade All") rightTitle:nil];
2723 }
2724
2725 - (size_t) count {
2726 return count_;
2727 }
2728
2729 - (void) setDelegate:(id)delegate {
2730 if (view_ != nil)
2731 [view_ setDelegate:delegate];
2732 [super setDelegate:delegate];
2733 }
2734
2735 @end
2736 /* }}} */
2737 /* Manage View {{{ */
2738 @interface ManageView : ResetView <
2739 PackageTableDelegate
2740 > {
2741 PackageTable *table_;
2742 PackageView *view_;
2743 NSString *package_;
2744 }
2745
2746 - (void) dealloc;
2747
2748 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2749
2750 - (void) packageTable:(id)table packageSelected:(Package *)package;
2751
2752 - (id) initWithFrame:(CGRect)frame;
2753 - (void) setPackages:(NSArray *)packages;
2754
2755 - (void) setDelegate:(id)delegate;
2756 @end
2757
2758 @implementation ManageView
2759
2760 - (void) dealloc {
2761 [table_ release];
2762 if (view_ != nil)
2763 [view_ release];
2764 if (package_ != nil)
2765 [package_ release];
2766 [super dealloc];
2767 }
2768
2769 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2770 if (button == 0) {
2771 [[view_ package] remove];
2772 [delegate_ resolve];
2773 [delegate_ perform];
2774 }
2775 }
2776
2777 - (void) packageTable:(id)table packageSelected:(Package *)package {
2778 if (package == nil) {
2779 [package_ release];
2780 package_ = nil;
2781
2782 [view_ release];
2783 view_ = nil;
2784 } else {
2785 _assert(package_ == nil);
2786 _assert(view_ == nil);
2787
2788 package_ = [[package name] retain];
2789
2790 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2791 [view_ setDelegate:delegate_];
2792
2793 [view_ setPackage:package];
2794
2795 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:@"Uninstall"];
2796 }
2797 }
2798
2799 - (id) initWithFrame:(CGRect)frame {
2800 if ((self = [super initWithFrame:frame]) != nil) {
2801 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2802 [table_ setDelegate:self];
2803
2804 [self pushView:table_ withTitle:@"Uninstall" backButtonTitle:@"Packages" rightButton:nil];
2805 } return self;
2806 }
2807
2808 - (void) setPackages:(NSArray *)packages {
2809 NSMutableArray *local = [NSMutableArray arrayWithCapacity:16];
2810 for (size_t i(0); i != [packages count]; ++i) {
2811 Package *package([packages objectAtIndex:i]);
2812 if ([package installed] != nil)
2813 [local addObject:package];
2814 }
2815
2816 [local sortUsingSelector:@selector(compareByName:)];
2817
2818 Package *npackage = nil;
2819 for (size_t offset = 0, count = [local count]; offset != count; ++offset) {
2820 Package *package = [local objectAtIndex:offset];
2821 if ([[package name] isEqualToString:package_])
2822 npackage = package;
2823 }
2824
2825 [table_ setPackages:local];
2826
2827 if (npackage != nil)
2828 [view_ setPackage:npackage];
2829 else if (package_ != nil)
2830 [self popViews:1];
2831
2832 [self setPrompt];
2833 }
2834
2835 - (void) setDelegate:(id)delegate {
2836 if (view_ != nil)
2837 [view_ setDelegate:delegate];
2838 [super setDelegate:delegate];
2839 }
2840
2841 @end
2842 /* }}} */
2843 /* Search View {{{ */
2844 @protocol SearchViewDelegate
2845 - (void) showKeyboard:(BOOL)show;
2846 @end
2847
2848 @interface SearchView : ResetView <
2849 PackageTableDelegate
2850 > {
2851 NSMutableArray *packages_;
2852 UIView *accessory_;
2853 UISearchField *field_;
2854 PackageTable *table_;
2855 PackageView *view_;
2856 NSString *package_;
2857 }
2858
2859 - (void) dealloc;
2860
2861 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2862 - (void) packageTable:(id)table packageSelected:(Package *)package;
2863
2864 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field;
2865 - (void) textFieldDidResignFirstResponder:(UITextField *)field;
2866
2867 - (void) keyboardInputChanged:(UIFieldEditor *)editor;
2868 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked;
2869
2870 - (id) initWithFrame:(CGRect)frame;
2871 - (void) setPackages:(NSArray *)packages;
2872
2873 - (void) setDelegate:(id)delegate;
2874 - (void) resetPackage:(Package *)package;
2875 - (void) searchPackages;
2876
2877 @end
2878
2879 @implementation SearchView
2880
2881 - (void) dealloc {
2882 [packages_ release];
2883 [accessory_ release];
2884 [field_ release];
2885 [table_ release];
2886 if (view_ != nil)
2887 [view_ release];
2888 if (package_ != nil)
2889 [package_ release];
2890 [super dealloc];
2891 }
2892
2893 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2894 if (button == 0) {
2895 Package *package = [view_ package];
2896 if ([package installed] == nil)
2897 [package install];
2898 else
2899 [package remove];
2900 [delegate_ resolve];
2901 [delegate_ perform];
2902 }
2903 }
2904
2905 - (void) packageTable:(id)table packageSelected:(Package *)package {
2906 if (package == nil) {
2907 [navbar_ setAccessoryView:accessory_ animate:(resetting_ ? NO : YES) goingBack:YES];
2908
2909 [package_ release];
2910 package_ = nil;
2911
2912 [view_ release];
2913 view_ = nil;
2914 } else {
2915 [navbar_ setAccessoryView:nil animate:YES goingBack:NO];
2916
2917 _assert(package_ == nil);
2918 _assert(view_ == nil);
2919
2920 package_ = [[package name] retain];
2921
2922 view_ = [[PackageView alloc] initWithFrame:[transition_ bounds]];
2923 [view_ setDelegate:delegate_];
2924
2925 [self pushView:view_ withTitle:[package name] backButtonTitle:nil rightButton:nil];
2926 [self resetPackage:package];
2927 }
2928 }
2929
2930 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2931 [delegate_ showKeyboard:YES];
2932 [table_ setEnabled:NO];
2933
2934 /*CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
2935 CGColor dimmed(alpha, 0, 0, 0, 0.5);
2936 [editor_ setBackgroundColor:dimmed];
2937 CGColorSpaceRelease(space);*/
2938 }
2939
2940 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
2941 [table_ setEnabled:YES];
2942 [delegate_ showKeyboard:NO];
2943 }
2944
2945 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
2946 NSString *text([field_ text]);
2947 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
2948 }
2949
2950 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
2951 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
2952 return YES;
2953
2954 [self searchPackages];
2955 [field_ resignFirstResponder];
2956 return NO;
2957 }
2958
2959 - (id) initWithFrame:(CGRect)frame {
2960 if ((self = [super initWithFrame:frame]) != nil) {
2961 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2962
2963 table_ = [[PackageTable alloc] initWithFrame:[transition_ bounds] versioner:@selector(latest)];
2964 [table_ setDelegate:self];
2965
2966 CGRect area = [self bounds];
2967 area.origin.y = 30;
2968 area.origin.x = 0;
2969 area.size.width -= 12;
2970 area.size.height = [UISearchField defaultHeight];
2971
2972 field_ = [[UISearchField alloc] initWithFrame:area];
2973
2974 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2975 [field_ setFont:font];
2976 CFRelease(font);
2977
2978 [field_ setPlaceholder:@"Package Names & Descriptions"];
2979 [field_ setPaddingTop:5];
2980 [field_ setDelegate:self];
2981
2982 UITextTraits *traits = [field_ textTraits];
2983 [traits setEditingDelegate:self];
2984 [traits setReturnKeyType:6];
2985
2986 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(6, 6, area.size.width, area.size.height + 30)];
2987 [accessory_ addSubview:field_];
2988
2989 [navbar_ setAccessoryView:accessory_];
2990 [self pushView:table_ withTitle:nil backButtonTitle:@"Search" rightButton:nil];
2991
2992 /* XXX: for the love of god just fix this */
2993 [navbar_ removeFromSuperview];
2994 [self addSubview:navbar_];
2995 } return self;
2996 }
2997
2998 - (void) setPackages:(NSArray *)packages {
2999 [packages_ removeAllObjects];
3000 [packages_ addObjectsFromArray:packages];
3001 [packages_ sortUsingSelector:@selector(compareByName:)];
3002
3003 Package *npackage = nil;
3004 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3005 Package *package = [packages_ objectAtIndex:offset];
3006 if ([[package name] isEqualToString:package_])
3007 npackage = package;
3008 }
3009
3010 [self searchPackages];
3011
3012 if (npackage != nil)
3013 [self resetPackage:npackage];
3014 else if (package_ != nil)
3015 [self popViews:1];
3016
3017 [self setPrompt];
3018 }
3019
3020 - (void) setDelegate:(id)delegate {
3021 if (view_ != nil)
3022 [view_ setDelegate:delegate];
3023 [super setDelegate:delegate];
3024 }
3025
3026 - (void) resetPackage:(Package *)package {
3027 [view_ setPackage:package];
3028 NSString *right = [package installed] == nil ? @"Install" : @"Uninstall";
3029 [navbar_ showButtonsWithLeftTitle:nil rightTitle:right];
3030 }
3031
3032 - (void) searchPackages {
3033 NSString *text([field_ text]);
3034
3035 NSMutableArray *packages([NSMutableArray arrayWithCapacity:16]);
3036
3037 for (size_t offset(0), count([packages_ count]); offset != count; ++offset) {
3038 Package *package = [packages_ objectAtIndex:offset];
3039 if ([package matches:text])
3040 [packages addObject:package];
3041 }
3042
3043 [table_ setPackages:packages];
3044 [[table_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3045 }
3046
3047 @end
3048 /* }}} */
3049
3050 @interface Cydia : UIApplication <
3051 ConfirmationViewDelegate,
3052 ProgressViewDelegate,
3053 SearchViewDelegate
3054 > {
3055 UIWindow *window_;
3056 UIView *underlay_;
3057 UIView *overlay_;
3058 UITransitionView *transition_;
3059 UIButtonBar *buttonbar_;
3060
3061 ConfirmationView *confirm_;
3062
3063 Database *database_;
3064 ProgressView *progress_;
3065
3066 UIView *featured_;
3067 UINavigationBar *navbar_;
3068 UIScroller *scroller_;
3069 UIWebView *webview_;
3070 NSURL *url_;
3071 UIProgressIndicator *indicator_;
3072
3073 InstallView *install_;
3074 ChangesView *changes_;
3075 ManageView *manage_;
3076 SearchView *search_;
3077
3078 bool restart_;
3079
3080 UIKeyboard *keyboard_;
3081 }
3082
3083 - (void) loadNews;
3084 - (void) reloadData:(BOOL)reset;
3085 - (void) setPrompt;
3086
3087 - (void) resolve;
3088 - (void) perform;
3089 - (void) upgrade;
3090 - (void) update;
3091
3092 - (void) cancel;
3093 - (void) confirm;
3094
3095 - (void) progressViewIsComplete:(ProgressView *)progress;
3096
3097 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
3098 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
3099 - (void) buttonBarItemTapped:(id)sender;
3100
3101 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old;
3102
3103 - (void) applicationWillSuspend;
3104 - (void) applicationDidFinishLaunching:(id)unused;
3105 @end
3106
3107 @implementation Cydia
3108
3109 - (void) loadNews {
3110 NSMutableURLRequest *request = [NSMutableURLRequest
3111 requestWithURL:url_
3112 cachePolicy:NSURLRequestReloadIgnoringCacheData
3113 timeoutInterval:30.0
3114 ];
3115
3116 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3117 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3118
3119 [webview_ loadRequest:request];
3120 [indicator_ startAnimation];
3121 }
3122
3123 - (void) reloadData:(BOOL)reset {
3124 [database_ reloadData];
3125
3126 size_t count = 16;
3127
3128 if (Packages_ == nil) {
3129 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
3130 [Metadata_ setObject:Packages_ forKey:@"Packages"];
3131 }
3132
3133 now_ = [NSDate date];
3134
3135 NSMutableArray *packages = [NSMutableArray arrayWithCapacity:count];
3136 for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
3137 if (Package *package = [Package packageWithIterator:iterator database:database_])
3138 [packages addObject:package];
3139
3140 [install_ setPackages:packages];
3141 [changes_ setPackages:packages];
3142 [manage_ setPackages:packages];
3143 [search_ setPackages:packages];
3144 //[self setPrompt];
3145
3146 if (size_t count = [changes_ count]) {
3147 NSString *badge([[NSNumber numberWithInt:count] stringValue]);
3148 [buttonbar_ setBadgeValue:badge forButton:3];
3149 [buttonbar_ setBadgeAnimated:YES forButton:3];
3150 [self setApplicationBadge:badge];
3151 } else {
3152 [buttonbar_ setBadgeValue:nil forButton:3];
3153 [buttonbar_ setBadgeAnimated:NO forButton:3];
3154 [self removeApplicationBadge];
3155 }
3156
3157 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
3158 }
3159
3160 - (void) setPrompt {
3161 [navbar_ setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3162 }
3163
3164 - (void) resolve {
3165 pkgProblemResolver *resolver = [database_ resolver];
3166
3167 resolver->InstallProtect();
3168 if (!resolver->Resolve(true))
3169 _error->Discard();
3170 }
3171
3172 - (void) perform {
3173 [database_ prepare];
3174 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
3175 }
3176
3177 - (void) upgrade {
3178 [database_ upgrade];
3179 [self perform];
3180 }
3181
3182 - (void) cancel {
3183 [self reloadData:NO];
3184 [confirm_ release];
3185 confirm_ = nil;
3186 }
3187
3188 - (void) confirm {
3189 [overlay_ removeFromSuperview];
3190 restart_ = true;
3191
3192 [progress_
3193 detachNewThreadSelector:@selector(perform)
3194 toTarget:database_
3195 withObject:nil
3196 title:@"Running..."
3197 ];
3198 }
3199
3200 - (void) bootstrap_ {
3201 [database_ update];
3202 [database_ upgrade];
3203 [database_ prepare];
3204 [database_ perform];
3205 }
3206
3207 - (void) bootstrap {
3208 [progress_
3209 detachNewThreadSelector:@selector(bootstrap_)
3210 toTarget:self
3211 withObject:nil
3212 title:@"Bootstrap Install..."
3213 ];
3214 }
3215
3216 - (void) update {
3217 [progress_
3218 detachNewThreadSelector:@selector(update)
3219 toTarget:database_
3220 withObject:nil
3221 title:@"Refreshing Sources..."
3222 ];
3223 }
3224
3225 - (void) progressViewIsComplete:(ProgressView *)progress {
3226 [self reloadData:YES];
3227
3228 if (confirm_ != nil) {
3229 [underlay_ addSubview:overlay_];
3230 [confirm_ removeFromSuperview];
3231 [confirm_ release];
3232 confirm_ = nil;
3233 }
3234 }
3235
3236 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
3237 switch (button) {
3238 case 0:
3239 [self loadNews];
3240 break;
3241
3242 case 1:
3243 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3244 initWithTitle:@"About Cydia Packager"
3245 buttons:[NSArray arrayWithObjects:@"Close", nil]
3246 defaultButtonIndex:0
3247 delegate:self
3248 context:self
3249 ] autorelease];
3250
3251 [sheet setBodyText:
3252 @"Copyright (C) 2008\n"
3253 "Jay Freeman (saurik)\n"
3254 "saurik@saurik.com\n"
3255 "http://www.saurik.com/\n"
3256 "\n"
3257 "The Okori Group\n"
3258 "http://www.theokorigroup.com/\n"
3259 "\n"
3260 "College of Creative Studies,\n"
3261 "University of California,\n"
3262 "Santa Barbara\n"
3263 "http://www.ccs.ucsb.edu/\n"
3264 "\n"
3265 "Special Thanks:\n"
3266 "bad_, BHSPitMonkey, cash, Cobra,\n"
3267 "core, Corona, crashx, cromas,\n"
3268 "Darken, dtzWill, Erica, francis,\n"
3269 "Godores, jerry, Kingstone, lounger,\n"
3270 "mbranes, rockabilly, tman, Wbiggs"
3271 ];
3272
3273 [sheet presentSheetFromButtonBar:buttonbar_];
3274 break;
3275 }
3276 }
3277
3278 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3279 [sheet dismiss];
3280 }
3281
3282 - (void) buttonBarItemTapped:(id)sender {
3283 UIView *view;
3284
3285 switch ([sender tag]) {
3286 case 1: view = featured_; break;
3287 case 2: view = install_; break;
3288 case 3: view = changes_; break;
3289 case 4: view = manage_; break;
3290 case 5: view = search_; break;
3291
3292 default:
3293 _assert(false);
3294 }
3295
3296 if ([view respondsToSelector:@selector(resetView)])
3297 [(id) view resetView];
3298 [transition_ transition:0 toView:view];
3299 }
3300
3301 - (void) view:(UIView *)view didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3302 [scroller_ setContentSize:frame.size];
3303 [indicator_ stopAnimation];
3304 }
3305
3306 - (void) applicationWillSuspend {
3307 if (restart_)
3308 system("launchctl stop com.apple.SpringBoard");
3309 }
3310
3311 - (void) applicationDidFinishLaunching:(id)unused {
3312 _assert(pkgInitConfig(*_config));
3313 _assert(pkgInitSystem(*_config, _system));
3314
3315 confirm_ = nil;
3316 restart_ = false;
3317
3318 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
3319 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
3320
3321 [window_ orderFront: self];
3322 [window_ makeKey: self];
3323 [window_ _setHidden: NO];
3324
3325 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
3326 [window_ setContentView:progress_];
3327
3328 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
3329 [progress_ setContentView:underlay_];
3330
3331 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
3332
3333 if (!bootstrap_)
3334 [underlay_ addSubview:overlay_];
3335
3336 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
3337 0, 0, screenrect.size.width, screenrect.size.height - 48
3338 )];
3339
3340 [overlay_ addSubview:transition_];
3341
3342 featured_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
3343
3344 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
3345 CGRect navrect = {{0, 0}, navsize};
3346
3347 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3348 [featured_ addSubview:navbar_];
3349
3350 [navbar_ setBarStyle:1];
3351 [navbar_ setDelegate:self];
3352 [navbar_ setPrompt:@"Welcome to Cydia Packager"];
3353
3354 [navbar_ showButtonsWithLeftTitle:@"About" rightTitle:@"Reload"];
3355
3356 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Featured"] autorelease];
3357 [navbar_ pushNavigationItem:navitem];
3358
3359 struct CGRect subbounds = [featured_ bounds];
3360 subbounds.origin.y += navsize.height;
3361 subbounds.size.height -= navsize.height;
3362
3363 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:subbounds] autorelease];
3364 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3365 [featured_ addSubview:pinstripe];
3366
3367 scroller_ = [[UIScroller alloc] initWithFrame:subbounds];
3368 [featured_ addSubview:scroller_];
3369
3370 [scroller_ setScrollingEnabled:YES];
3371 [scroller_ setAdjustForContentSizeChange:YES];
3372 [scroller_ setClipsSubviews:YES];
3373 [scroller_ setAllowsRubberBanding:YES];
3374 [scroller_ setScrollDecelerationFactor:0.99];
3375 [scroller_ setDelegate:self];
3376
3377 webview_ = [[UIWebView alloc] initWithFrame:[scroller_ bounds]];
3378 [scroller_ addSubview:webview_];
3379
3380 [webview_ setTilingEnabled:YES];
3381 [webview_ setTileSize:CGSizeMake(screenrect.size.width, 500)];
3382 [webview_ setAutoresizes:YES];
3383 [webview_ setDelegate:self];
3384
3385 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3386 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(87, 45, indsize.width, indsize.height)];
3387 [indicator_ setStyle:2];
3388 [featured_ addSubview:indicator_];
3389
3390 NSArray *buttonitems = [NSArray arrayWithObjects:
3391 [NSDictionary dictionaryWithObjectsAndKeys:
3392 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3393 @"featured-up.png", kUIButtonBarButtonInfo,
3394 @"featured-dn.png", kUIButtonBarButtonSelectedInfo,
3395 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
3396 self, kUIButtonBarButtonTarget,
3397 @"Featured", kUIButtonBarButtonTitle,
3398 @"0", kUIButtonBarButtonType,
3399 nil],
3400
3401 [NSDictionary dictionaryWithObjectsAndKeys:
3402 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3403 @"install-up.png", kUIButtonBarButtonInfo,
3404 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
3405 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
3406 self, kUIButtonBarButtonTarget,
3407 @"Install", kUIButtonBarButtonTitle,
3408 @"0", kUIButtonBarButtonType,
3409 nil],
3410
3411 [NSDictionary dictionaryWithObjectsAndKeys:
3412 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3413 @"changes-up.png", kUIButtonBarButtonInfo,
3414 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
3415 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
3416 self, kUIButtonBarButtonTarget,
3417 @"Changes", kUIButtonBarButtonTitle,
3418 @"0", kUIButtonBarButtonType,
3419 nil],
3420
3421 [NSDictionary dictionaryWithObjectsAndKeys:
3422 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3423 @"manage-up.png", kUIButtonBarButtonInfo,
3424 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
3425 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
3426 self, kUIButtonBarButtonTarget,
3427 @"Uninstall", kUIButtonBarButtonTitle,
3428 @"0", kUIButtonBarButtonType,
3429 nil],
3430
3431 [NSDictionary dictionaryWithObjectsAndKeys:
3432 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
3433 @"search-up.png", kUIButtonBarButtonInfo,
3434 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
3435 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
3436 self, kUIButtonBarButtonTarget,
3437 @"Search", kUIButtonBarButtonTitle,
3438 @"0", kUIButtonBarButtonType,
3439 nil],
3440 nil];
3441
3442 buttonbar_ = [[UIButtonBar alloc]
3443 initInView:overlay_
3444 withFrame:CGRectMake(
3445 0, screenrect.size.height - 48,
3446 screenrect.size.width, 48
3447 )
3448 withItemList:buttonitems
3449 ];
3450
3451 [buttonbar_ setDelegate:self];
3452 [buttonbar_ setBarStyle:1];
3453 [buttonbar_ setButtonBarTrackingMode:2];
3454
3455 int buttons[5] = {1, 2, 3, 4, 5};
3456 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
3457 [buttonbar_ showButtonGroup:0 withDuration:0];
3458
3459 for (int i = 0; i != 5; ++i)
3460 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
3461 i * 64 + 2, 1, 60, 48
3462 )];
3463
3464 [buttonbar_ showSelectionForButton:1];
3465 [transition_ transition:0 toView:featured_];
3466
3467 [overlay_ addSubview:buttonbar_];
3468
3469 [UIKeyboard initImplementationNow];
3470
3471 CGRect edtrect = [overlay_ bounds];
3472 edtrect.origin.y += navsize.height;
3473 edtrect.size.height -= navsize.height;
3474
3475 CGSize keysize = [UIKeyboard defaultSize];
3476 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
3477 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
3478
3479 database_ = [[Database alloc] init];
3480 [database_ setDelegate:progress_];
3481
3482 install_ = [[InstallView alloc] initWithFrame:[transition_ bounds]];
3483 [install_ setDelegate:self];
3484
3485 changes_ = [[ChangesView alloc] initWithFrame:[transition_ bounds]];
3486 [changes_ setDelegate:self];
3487
3488 manage_ = [[ManageView alloc] initWithFrame:[transition_ bounds]];
3489 [manage_ setDelegate:self];
3490
3491 search_ = [[SearchView alloc] initWithFrame:[transition_ bounds]];
3492 [search_ setDelegate:self];
3493
3494 [self reloadData:NO];
3495
3496 Package *package([database_ packageWithName:@"cydia"]);
3497 NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
3498 WebView *webview = [webview_ webView];
3499 [webview setApplicationNameForUserAgent:application];
3500
3501 url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
3502 [self loadNews];
3503
3504 [progress_ resetView];
3505
3506 if (bootstrap_)
3507 [self bootstrap];
3508 }
3509
3510 - (void) showKeyboard:(BOOL)show {
3511 if (show)
3512 [overlay_ addSubview:keyboard_];
3513 else
3514 [keyboard_ removeFromSuperview];
3515 }
3516
3517 @end
3518
3519 void AddPreferences(NSString *plist) {
3520 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3521
3522 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
3523 _assert(settings != NULL);
3524 NSMutableArray *items = [settings objectForKey:@"items"];
3525
3526 bool cydia(false);
3527
3528 for (size_t i(0); i != [items count]; ++i) {
3529 NSMutableDictionary *item([items objectAtIndex:i]);
3530 NSString *label = [item objectForKey:@"label"];
3531 if (label != nil && [label isEqualToString:@"Cydia"]) {
3532 cydia = true;
3533 break;
3534 }
3535 }
3536
3537 if (!cydia) {
3538 for (size_t i(0); i != [items count]; ++i) {
3539 NSDictionary *item([items objectAtIndex:i]);
3540 NSString *label = [item objectForKey:@"label"];
3541 if (label != nil && [label isEqualToString:@"General"]) {
3542 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
3543 @"CydiaSettings", @"bundle",
3544 @"PSLinkCell", @"cell",
3545 [NSNumber numberWithBool:YES], @"hasIcon",
3546 [NSNumber numberWithBool:YES], @"isController",
3547 @"Cydia", @"label",
3548 nil] atIndex:(i + 1)];
3549
3550 break;
3551 }
3552 }
3553
3554 _assert([settings writeToFile:plist atomically:YES] == YES);
3555 }
3556
3557 [pool release];
3558 }
3559
3560 /*IMP alloc_;
3561 id Alloc_(id self, SEL selector) {
3562 id object = alloc_(self, selector);
3563 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
3564 return object;
3565 }*/
3566
3567 int main(int argc, char *argv[]) {
3568 struct nlist nl[2];
3569 memset(nl, 0, sizeof(nl));
3570 nl[0].n_un.n_name = "_useMDNSResponder";
3571 nlist("/usr/lib/libc.dylib", nl);
3572 if (nl[0].n_type != N_UNDF)
3573 *(int *) nl[0].n_value = 0;
3574
3575 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
3576
3577 setuid(0);
3578 setgid(0);
3579
3580 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
3581 alloc_ = alloc->method_imp;
3582 alloc->method_imp = (IMP) &Alloc_;*/
3583
3584 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3585
3586 size_t size;
3587 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
3588 char *machine = new char[size];
3589 sysctlbyname("hw.machine", machine, &size, NULL, 0);
3590 Machine_ = machine;
3591
3592 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
3593 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
3594 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
3595 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
3596 CFRelease(serial);
3597 }
3598
3599 IOObjectRelease(service);
3600 }
3601
3602 AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
3603 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");
3604
3605 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
3606 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
3607 else
3608 Packages_ = [Metadata_ objectForKey:@"Packages"];
3609
3610 UIApplicationMain(argc, argv, [Cydia class]);
3611 [pool release];
3612 return 0;
3613 }