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