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