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