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