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