]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Swapping out for new Cydia icon.
[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 #include <errno.h>
24 #include <pcre.h>
25 #include <string.h>
26 /* }}} */
27 /* Extension Keywords {{{ */
28 #define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
29
30 #define _assert(test) do \
31 if (!(test)) { \
32 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
33 exit(-1); \
34 } \
35 while (false)
36 /* }}} */
37
38 @interface WebView
39 - (void) setApplicationNameForUserAgent:(NSString *)applicationName;
40 @end
41
42 static const int PulseInterval_ = 50000;
43 const char *Machine_ = NULL;
44 const char *SerialNumber_ = NULL;
45
46 @interface NSString (CydiaBypass)
47 - (NSString *) stringByAddingPercentEscapes;
48 @end
49
50 @protocol ProgressDelegate
51 - (void) setError:(NSString *)error;
52 - (void) setTitle:(NSString *)title;
53 - (void) setPercent:(float)percent;
54 - (void) addOutput:(NSString *)output;
55 @end
56
57 NSString *SizeString(double size) {
58 unsigned power = 0;
59 while (size > 1024) {
60 size /= 1024;
61 ++power;
62 }
63
64 static const char *powers_[] = {"B", "kB", "MB", "GB"};
65
66 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
67 }
68
69 /* Status Delegation {{{ */
70 class Status :
71 public pkgAcquireStatus
72 {
73 private:
74 id delegate_;
75
76 public:
77 Status() :
78 delegate_(nil)
79 {
80 }
81
82 void setDelegate(id delegate) {
83 delegate_ = delegate;
84 }
85
86 virtual bool MediaChange(std::string media, std::string drive) {
87 return false;
88 }
89
90 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
91 }
92
93 virtual void Fetch(pkgAcquire::ItemDesc &item) {
94 [delegate_ setTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
95 }
96
97 virtual void Done(pkgAcquire::ItemDesc &item) {
98 }
99
100 virtual void Fail(pkgAcquire::ItemDesc &item) {
101 [delegate_ performSelectorOnMainThread:@selector(setStatusFail) withObject:nil waitUntilDone:YES];
102 }
103
104 virtual bool Pulse(pkgAcquire *Owner) {
105 bool value = pkgAcquireStatus::Pulse(Owner);
106
107 float percent(
108 double(CurrentBytes + CurrentItems) /
109 double(TotalBytes + TotalItems)
110 );
111
112 [delegate_ setPercent:percent];
113 return value;
114 }
115
116 virtual void Start() {
117 }
118
119 virtual void Stop() {
120 }
121 };
122 /* }}} */
123 /* Progress Delegation {{{ */
124 class Progress :
125 public OpProgress
126 {
127 private:
128 id delegate_;
129
130 protected:
131 virtual void Update() {
132 [delegate_ setTitle:[NSString stringWithCString:Op.c_str()]];
133 [delegate_ setPercent:(Percent / 100)];
134 }
135
136 public:
137 Progress() :
138 delegate_(nil)
139 {
140 }
141
142 void setDelegate(id delegate) {
143 delegate_ = delegate;
144 }
145
146 virtual void Done() {
147 [delegate_ setPercent:1];
148 }
149 };
150 /* }}} */
151
152 /* External Constants {{{ */
153 extern NSString *kUIButtonBarButtonAction;
154 extern NSString *kUIButtonBarButtonInfo;
155 extern NSString *kUIButtonBarButtonInfoOffset;
156 extern NSString *kUIButtonBarButtonSelectedInfo;
157 extern NSString *kUIButtonBarButtonStyle;
158 extern NSString *kUIButtonBarButtonTag;
159 extern NSString *kUIButtonBarButtonTarget;
160 extern NSString *kUIButtonBarButtonTitle;
161 extern NSString *kUIButtonBarButtonTitleVerticalHeight;
162 extern NSString *kUIButtonBarButtonTitleWidth;
163 extern NSString *kUIButtonBarButtonType;
164 /* }}} */
165 /* Mime Addresses {{{ */
166 @interface Address : NSObject {
167 NSString *name_;
168 NSString *email_;
169 }
170
171 - (void) dealloc;
172
173 - (NSString *) name;
174 - (NSString *) email;
175
176 + (Address *) addressWithString:(NSString *)string;
177 - (Address *) initWithString:(NSString *)string;
178 @end
179
180 @implementation Address
181
182 - (void) dealloc {
183 [name_ release];
184 if (email_ != nil)
185 [email_ release];
186 [super dealloc];
187 }
188
189 - (NSString *) name {
190 return name_;
191 }
192
193 - (NSString *) email {
194 return email_;
195 }
196
197 + (Address *) addressWithString:(NSString *)string {
198 return [[[Address alloc] initWithString:string] autorelease];
199 }
200
201 - (Address *) initWithString:(NSString *)string {
202 if ((self = [super init]) != nil) {
203 const char *error;
204 int offset;
205 pcre *code = pcre_compile("^\"?(.*)\"? <([^>]*)>$", 0, &error, &offset, NULL);
206
207 if (code == NULL) {
208 fprintf(stderr, "%d:%s\n", offset, error);
209 _assert(false);
210 }
211
212 pcre_extra *study = NULL;
213 int capture;
214 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
215 int matches[(capture + 1) * 3];
216
217 size_t size = [string length];
218 const char *data = [string UTF8String];
219
220 if (pcre_exec(code, study, data, size, 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0) {
221 name_ = [[NSString stringWithCString:(data + matches[2]) length:(matches[3] - matches[2])] retain];
222 email_ = [[NSString stringWithCString:(data + matches[4]) length:(matches[5] - matches[4])] retain];
223 } else {
224 name_ = [[NSString stringWithCString:data length:size] retain];
225 email_ = nil;
226 }
227 } return self;
228 }
229
230 @end
231 /* }}} */
232
233 /* Right Alignment {{{ */
234 @interface UIRightTextLabel : UITextLabel {
235 float _savedRightEdgeX;
236 BOOL _sizedtofit_flag;
237 }
238
239 - (void) setFrame:(CGRect)frame;
240 - (void) setText:(NSString *)text;
241 - (void) realignText;
242 @end
243
244 @implementation UIRightTextLabel
245
246 - (void) setFrame:(CGRect)frame {
247 [super setFrame:frame];
248 if (_sizedtofit_flag == NO) {
249 _savedRightEdgeX = frame.origin.x;
250 [self realignText];
251 }
252 }
253
254 - (void) setText:(NSString *)text {
255 [super setText:text];
256 [self realignText];
257 }
258
259 - (void) realignText {
260 CGRect oldFrame = [self frame];
261
262 _sizedtofit_flag = YES;
263 [self sizeToFit]; // shrink down size so I can right align it
264
265 CGRect newFrame = [self frame];
266
267 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
268 oldFrame.size.width = newFrame.size.width;
269 [super setFrame:oldFrame];
270 _sizedtofit_flag = NO;
271 }
272
273 @end
274 /* }}} */
275 /* Linear Algebra {{{ */
276 inline float interpolate(float begin, float end, float fraction) {
277 return (end - begin) * fraction + begin;
278 }
279 /* }}} */
280
281 @class Package;
282
283 @interface Database : NSObject {
284 pkgCacheFile cache_;
285 pkgRecords *records_;
286 pkgProblemResolver *resolver_;
287 pkgAcquire *fetcher_;
288 FileFd *lock_;
289 SPtr<pkgPackageManager> manager_;
290
291 id delegate_;
292 Status status_;
293 Progress progress_;
294 int statusfd_;
295 }
296
297 - (void) _readStatus:(NSNumber *)fd;
298 - (void) _readOutput:(NSNumber *)fd;
299
300 - (Package *) packageWithName:(NSString *)name;
301
302 - (Database *) init;
303 - (pkgCacheFile &) cache;
304 - (pkgRecords *) records;
305 - (pkgProblemResolver *) resolver;
306 - (pkgAcquire &) fetcher;
307 - (void) reloadData;
308
309 - (void) prepare;
310 - (void) perform;
311 - (void) update;
312 - (void) upgrade;
313
314 - (void) setDelegate:(id)delegate;
315 @end
316
317 /* Reset View {{{ */
318 @interface ResetView : UIView {
319 UINavigationBar *navbar_;
320 bool resetting_;
321 }
322
323 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
324
325 - (void) dealloc;
326 - (void) resetView;
327 - (void) _resetView;
328 - (NSString *) leftTitle;
329 - (NSString *) rightTitle;
330 @end
331
332 @implementation ResetView
333
334 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
335 if ([[navbar_ navigationItems] count] == 1)
336 [self _resetView];
337 }
338
339 - (void) dealloc {
340 [navbar_ release];
341 [super dealloc];
342 }
343
344 - (void) resetView {
345 resetting_ = true;
346 if ([[navbar_ navigationItems] count] == 1)
347 [self _resetView];
348 else while ([[navbar_ navigationItems] count] != 1)
349 [navbar_ popNavigationItem];
350 resetting_ = false;
351 }
352
353 - (void) _resetView {
354 [navbar_ showButtonsWithLeftTitle:[self leftTitle] rightTitle:[self rightTitle]];
355 }
356
357 - (NSString *) leftTitle {
358 return nil;
359 }
360
361 - (NSString *) rightTitle {
362 return nil;
363 }
364
365 @end
366 /* }}} */
367 /* Confirmation View {{{ */
368 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
369 if ([packages count] == 0)
370 return;
371
372 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
373 float clear[] = {0, 0, 0, 0};
374 float blue[] = {0, 0, 0.4, 1};
375
376 UITextView *text([[[UITextView alloc] initWithFrame: CGRectMake(110, 3, 200, 60)] autorelease]);
377 [text setEditable:NO];
378 [text setTextSize:16];
379 [text setBackgroundColor:CGColorCreate(space, clear)];
380 [text setTextColor:CGColorCreate(space, blue)];
381 [text setText:([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "])];
382 [text setEnabled:NO];
383
384 CGRect frame([text frame]);
385 CGSize size([text contentSize]);
386 frame.size.height = size.height;
387 [text setFrame:frame];
388
389 [fields setObject:text forKey:key];
390 }
391
392 @protocol ConfirmationViewDelegate
393 - (void) cancel;
394 - (void) confirm;
395 @end
396
397 @interface ConfirmationView : UIView {
398 Database *database_;
399 id delegate_;
400 UITransitionView *transition_;
401 UIView *overlay_;
402 UINavigationBar *navbar_;
403 UIPreferencesTable *table_;
404 NSMutableDictionary *fields_;
405 UIAlertSheet *essential_;
406 }
407
408 - (void) dealloc;
409 - (void) cancel;
410
411 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
412 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
413 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
414
415 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
416 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
417 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed;
418 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
419 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
420
421 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
422
423 @end
424
425 @implementation ConfirmationView
426
427 - (void) dealloc {
428 [transition_ release];
429 [overlay_ release];
430 [navbar_ release];
431 [table_ release];
432 [fields_ release];
433 if (essential_ != nil)
434 [essential_ release];
435 [super dealloc];
436 }
437
438 - (void) cancel {
439 [transition_ transition:7 toView:nil];
440 [delegate_ cancel];
441 }
442
443 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
444 if (from != nil && to == nil)
445 [self removeFromSuperview];
446 }
447
448 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
449 switch (button) {
450 case 0:
451 if (essential_ != nil)
452 [essential_ popupAlertAnimated:YES];
453 else
454 [delegate_ confirm];
455 break;
456
457 case 1:
458 [self cancel];
459 break;
460 }
461 }
462
463 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
464 [essential_ dismiss];
465 [self cancel];
466 }
467
468 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
469 return 2;
470 }
471
472 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
473 switch (group) {
474 case 0: return @"Statistics";
475 case 1: return @"Modifications";
476
477 default: _assert(false);
478 }
479 }
480
481 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
482 switch (group) {
483 case 0: return 3;
484 case 1: return [fields_ count];
485
486 default: _assert(false);
487 }
488 }
489
490 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
491 if (group != 1 || row == -1)
492 return proposed;
493 else {
494 _assert(size_t(row) < [fields_ count]);
495 return [[[fields_ allValues] objectAtIndex:row] contentSize].height;
496 }
497 }
498
499 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
500 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
501 [cell setShowSelection:NO];
502
503 switch (group) {
504 case 0: switch (row) {
505 case 0: {
506 [cell setTitle:@"Downloading"];
507 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
508 } break;
509
510 case 1: {
511 [cell setTitle:@"Resuming At"];
512 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
513 } break;
514
515 case 2: {
516 double size([database_ cache]->UsrSize());
517
518 if (size < 0) {
519 [cell setTitle:@"Disk Freeing"];
520 [cell setValue:SizeString(-size)];
521 } else {
522 [cell setTitle:@"Disk Using"];
523 [cell setValue:SizeString(size)];
524 }
525 } break;
526
527 default: _assert(false);
528 } break;
529
530 case 1:
531 _assert(size_t(row) < [fields_ count]);
532 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
533 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
534 break;
535
536 default: _assert(false);
537 }
538
539 return cell;
540 }
541
542 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
543 if ((self = [super initWithFrame:[view bounds]]) != nil) {
544 database_ = database;
545 delegate_ = delegate;
546
547 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
548 [self addSubview:transition_];
549
550 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
551
552 CGSize navsize = [UINavigationBar defaultSize];
553 CGRect navrect = {{0, 0}, navsize};
554 CGRect bounds = [overlay_ bounds];
555
556 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
557 [navbar_ setBarStyle:1];
558 [navbar_ setDelegate:self];
559
560 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
561 [navbar_ pushNavigationItem:navitem];
562 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
563
564 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
565
566 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
567 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
568 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
569
570 bool essential(false);
571
572 pkgCacheFile &cache([database_ cache]);
573 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
574 NSString *name([NSString stringWithCString:iterator.Name()]);
575 if (cache[iterator].NewInstall())
576 [installing addObject:name];
577 else if (cache[iterator].Upgrade())
578 [upgrading addObject:name];
579 else if (cache[iterator].Delete()) {
580 [removing addObject:name];
581 if ((iterator->Flags & pkgCache::Flag::Essential) != 0)
582 essential = true;
583 }
584 }
585
586 if (!essential)
587 essential_ = nil;
588 else {
589 essential_ = [[UIAlertSheet alloc]
590 initWithTitle:@"Unable to Comply"
591 buttons:[NSArray arrayWithObjects:@"Okay", nil]
592 defaultButtonIndex:0
593 delegate:self
594 context:self
595 ];
596
597 [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."];
598 }
599
600 AddTextView(fields_, installing, @"Installing");
601 AddTextView(fields_, upgrading, @"Upgrading");
602 AddTextView(fields_, removing, @"Removing");
603
604 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
605 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
606 )];
607
608 [table_ setReusesTableCells:YES];
609 [table_ setDataSource:self];
610 [table_ reloadData];
611
612 [overlay_ addSubview:navbar_];
613 [overlay_ addSubview:table_];
614
615 [view addSubview:self];
616
617 [transition_ setDelegate:self];
618
619 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
620 [transition_ transition:0 toView:blank];
621 [transition_ transition:3 toView:overlay_];
622 } return self;
623 }
624
625 @end
626 /* }}} */
627
628 /* Package Class {{{ */
629 @interface Package : NSObject {
630 pkgCache::PkgIterator iterator_;
631 Database *database_;
632 pkgRecords::Parser *parser_;
633 pkgCache::VerIterator version_;
634 pkgCache::VerFileIterator file_;
635 }
636
637 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
638 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
639
640 - (NSString *) name;
641 - (NSString *) section;
642 - (NSString *) latest;
643 - (NSString *) installed;
644 - (Address *) maintainer;
645 - (size_t) size;
646 - (NSString *) tagline;
647 - (NSString *) description;
648 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
649
650 - (void) install;
651 - (void) remove;
652 @end
653
654 @implementation Package
655
656 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
657 if ((self = [super init]) != nil) {
658 iterator_ = iterator;
659 database_ = database;
660
661 version_ = version;
662 file_ = file;
663 parser_ = &[database_ records]->Lookup(file);
664 } return self;
665 }
666
667 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
668 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
669 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
670 return [[[Package alloc]
671 initWithIterator:iterator
672 database:database
673 version:version
674 file:file]
675 autorelease];
676 return nil;
677 }
678
679 - (NSString *) name {
680 return [[NSString stringWithCString:iterator_.Name()] lowercaseString];
681 }
682
683 - (NSString *) section {
684 return [NSString stringWithCString:iterator_.Section()];
685 }
686
687 - (NSString *) latest {
688 return [NSString stringWithCString:version_.VerStr()];
689 }
690
691 - (NSString *) installed {
692 return iterator_.CurrentVer().end() ? nil : [NSString stringWithCString:iterator_.CurrentVer().VerStr()];
693 }
694
695 - (Address *) maintainer {
696 return [Address addressWithString:[NSString stringWithCString:parser_->Maintainer().c_str()]];
697 }
698
699 - (size_t) size {
700 return version_->InstalledSize;
701 }
702
703 - (NSString *) tagline {
704 return [NSString stringWithCString:parser_->ShortDesc().c_str()];
705 }
706
707 - (NSString *) description {
708 return [NSString stringWithCString:parser_->LongDesc().c_str()];
709 }
710
711 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
712 NSComparisonResult result = [[self section] compare:[package section]];
713 if (result != NSOrderedSame)
714 return result;
715 return [[self name] compare:[package name]];
716 }
717
718 - (void) install {
719 pkgProblemResolver *resolver = [database_ resolver];
720 resolver->Clear(iterator_);
721 resolver->Protect(iterator_);
722 [database_ cache]->MarkInstall(iterator_, false);
723 }
724
725 - (void) remove {
726 pkgProblemResolver *resolver = [database_ resolver];
727 resolver->Clear(iterator_);
728 resolver->Protect(iterator_);
729 resolver->Remove(iterator_);
730 [database_ cache]->MarkDelete(iterator_, true);
731 }
732
733 @end
734 /* }}} */
735 /* Section Class {{{ */
736 @interface Section : NSObject {
737 NSString *name_;
738 size_t row_;
739 NSMutableArray *packages_;
740 }
741
742 - (void) dealloc;
743
744 - (Section *) initWithName:(NSString *)name row:(size_t)row;
745 - (NSString *) name;
746 - (size_t) row;
747 - (void) addPackage:(Package *)package;
748 @end
749
750 @implementation Section
751
752 - (void) dealloc {
753 [name_ release];
754 [packages_ release];
755 [super dealloc];
756 }
757
758 - (Section *) initWithName:(NSString *)name row:(size_t)row {
759 if ((self = [super init]) != nil) {
760 name_ = [name retain];
761 row_ = row;
762 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
763 } return self;
764 }
765
766 - (NSString *) name {
767 return name_;
768 }
769
770 - (size_t) row {
771 return row_;
772 }
773
774 - (void) addPackage:(Package *)package {
775 [packages_ addObject:package];
776 }
777
778 @end
779 /* }}} */
780
781 /* Package View {{{ */
782 @interface PackageView : UIView {
783 UIPreferencesTable *table_;
784 Package *package_;
785 Database *database_;
786 NSMutableArray *cells_;
787 id delegate_;
788 }
789
790 - (void) dealloc;
791
792 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table;
793 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group;
794 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group;
795 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group;
796
797 - (BOOL) canSelectRow:(int)row;
798 - (void) tableRowSelected:(NSNotification *)notification;
799
800 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database;
801 - (void) setPackage:(Package *)package;
802 - (void) setDelegate:(id)delegate;
803 @end
804
805 @implementation PackageView
806
807 - (void) dealloc {
808 if (package_ != nil)
809 [package_ release];
810 [table_ release];
811 [database_ release];
812 [cells_ release];
813 [super dealloc];
814 }
815
816 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
817 return 2;
818 }
819
820 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
821 switch (group) {
822 case 0: return @"Specifics";
823 case 1: return @"Description";
824
825 default: _assert(false);
826 }
827 }
828
829 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
830 switch (group) {
831 case 0: return 6;
832 case 1: return 1;
833
834 default: _assert(false);
835 }
836 }
837
838 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
839 UIPreferencesTableCell *cell;
840
841 switch (group) {
842 case 0: switch (row) {
843 case 0:
844 cell = [cells_ objectAtIndex:0];
845 [cell setTitle:@"Name"];
846 [cell setValue:[package_ name]];
847 break;
848
849 case 1: {
850 cell = [cells_ objectAtIndex:1];
851 [cell setTitle:@"Installed"];
852 NSString *installed([package_ installed]);
853 [cell setValue:(installed == nil ? @"n/a" : installed)];
854 } break;
855
856 case 2:
857 cell = [cells_ objectAtIndex:2];
858 [cell setTitle:@"Latest"];
859 [cell setValue:[package_ latest]];
860 break;
861
862 case 3:
863 cell = [cells_ objectAtIndex:3];
864 [cell setTitle:@"Section"];
865 [cell setValue:[package_ section]];
866 break;
867
868 case 4:
869 cell = [cells_ objectAtIndex:4];
870 [cell setTitle:@"Size"];
871 [cell setValue:SizeString([package_ size])];
872 break;
873
874 case 5:
875 cell = [cells_ objectAtIndex:5];
876 [cell setTitle:@"Maintainer"];
877 [cell setValue:[[package_ maintainer] name]];
878 [cell setShowDisclosure:YES];
879 [cell setShowSelection:YES];
880 break;
881
882 default: _assert(false);
883 } break;
884
885 case 1: switch (row) {
886 case 0:
887 cell = [cells_ objectAtIndex:6];
888 [cell setTitle:nil];
889 [cell setValue:[package_ tagline]];
890 break;
891
892 case 1:
893 cell = [cells_ objectAtIndex:7];
894 [cell setTitle:@"Description"];
895 [cell setValue:[package_ description]];
896 break;
897 } break;
898
899 default: _assert(false);
900 }
901
902 return cell;
903 }
904
905 - (BOOL) canSelectRow:(int)row {
906 return YES;
907 }
908
909 - (void) tableRowSelected:(NSNotification *)notification {
910 switch ([table_ selectedRow]) {
911 case 5:
912 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
913 [[package_ maintainer] email],
914 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
915 ]]];
916 break;
917 }
918 }
919
920 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database {
921 if ((self = [super initWithFrame:frame]) != nil) {
922 database_ = [database retain];
923
924 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
925 [self addSubview:table_];
926
927 [table_ setDataSource:self];
928 [table_ setDelegate:self];
929
930 cells_ = [[NSMutableArray arrayWithCapacity:16] retain];
931
932 for (unsigned i = 0; i != 8; ++i) {
933 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
934 [cell setShowSelection:NO];
935 [cells_ addObject:cell];
936 }
937 } return self;
938 }
939
940 - (void) setPackage:(Package *)package {
941 package_ = [package retain];
942 [table_ reloadData];
943 }
944
945 - (void) setDelegate:(id)delegate {
946 delegate_ = delegate;
947 }
948
949 @end
950 /* }}} */
951 /* Package Cell {{{ */
952 @protocol PackageCellDelegate
953 - (NSString *) versionWithPackage:(Package *)package;
954 @end
955
956 @interface PackageCell : UITableCell {
957 UITextLabel *name_;
958 UIRightTextLabel *version_;
959 UITextLabel *description_;
960 }
961
962 - (void) dealloc;
963
964 - (PackageCell *) initWithPackage:(Package *)package delegate:(id)delegate;
965
966 - (void) _setSelected:(float)fraction;
967 - (void) setSelected:(BOOL)selected;
968 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
969 - (void) _setSelectionFadeFraction:(float)fraction;
970
971 @end
972
973 @implementation PackageCell
974
975 - (void) dealloc {
976 [name_ release];
977 [version_ release];
978 [description_ release];
979 [super dealloc];
980 }
981
982 - (PackageCell *) initWithPackage:(Package *)package delegate:(id)delegate {
983 if ((self = [super init]) != nil) {
984 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
985 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
986 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
987
988 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
989 float clear[] = {0, 0, 0, 0};
990
991 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 250, 25)];
992 [name_ setBackgroundColor:CGColorCreate(space, clear)];
993 [name_ setFont:bold];
994 [name_ setText:[package name]];
995
996 version_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 7, 70, 25)];
997 [version_ setBackgroundColor:CGColorCreate(space, clear)];
998 [version_ setFont:large];
999 [version_ setText:[delegate versionWithPackage:package]];
1000
1001 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
1002 [description_ setBackgroundColor:CGColorCreate(space, clear)];
1003 [description_ setFont:small];
1004 [description_ setText:[package tagline]];
1005
1006 [self addSubview:name_];
1007 [self addSubview:version_];
1008 [self addSubview:description_];
1009
1010 CFRelease(small);
1011 CFRelease(large);
1012 CFRelease(bold);
1013 } return self;
1014 }
1015
1016 - (void) _setSelected:(float)fraction {
1017 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1018
1019 float black[] = {
1020 interpolate(0.0, 1.0, fraction),
1021 interpolate(0.0, 1.0, fraction),
1022 interpolate(0.0, 1.0, fraction),
1023 1.0};
1024
1025 float blue[] = {
1026 interpolate(0.2, 1.0, fraction),
1027 interpolate(0.2, 1.0, fraction),
1028 interpolate(1.0, 1.0, fraction),
1029 1.0};
1030
1031 float gray[] = {
1032 interpolate(0.4, 1.0, fraction),
1033 interpolate(0.4, 1.0, fraction),
1034 interpolate(0.4, 1.0, fraction),
1035 1.0};
1036
1037 [name_ setColor:CGColorCreate(space, black)];
1038 [version_ setColor:CGColorCreate(space, blue)];
1039 [description_ setColor:CGColorCreate(space, gray)];
1040 }
1041
1042 - (void) setSelected:(BOOL)selected {
1043 [self _setSelected:(selected ? 1.0 : 0.0)];
1044 [super setSelected:selected];
1045 }
1046
1047 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
1048 if (!fade)
1049 [self _setSelected:(selected ? 1.0 : 0.0)];
1050 [super setSelected:selected withFade:fade];
1051 }
1052
1053 - (void) _setSelectionFadeFraction:(float)fraction {
1054 [self _setSelected:fraction];
1055 [super _setSelectionFadeFraction:fraction];
1056 }
1057
1058 @end
1059 /* }}} */
1060
1061 /* Source {{{ */
1062 @interface Source : NSObject {
1063 NSString *description_;
1064 NSString *label_;
1065 NSString *origin_;
1066
1067 NSString *uri_;
1068 NSString *distribution_;
1069 NSString *type_;
1070
1071 BOOL trusted_;
1072 }
1073
1074 - (void) dealloc;
1075
1076 - (Source *) initWithMetaIndex:(metaIndex *)index;
1077
1078 - (BOOL) trusted;
1079
1080 - (NSString *) uri;
1081 - (NSString *) distribution;
1082 - (NSString *) type;
1083
1084 - (NSString *) description;
1085 - (NSString *) label;
1086 - (NSString *) origin;
1087 @end
1088
1089 @implementation Source
1090
1091 - (void) dealloc {
1092 [uri_ release];
1093 [distribution_ release];
1094 [type_ release];
1095
1096 if (description_ != nil)
1097 [description_ release];
1098 if (label_ != nil)
1099 [label_ release];
1100 if (origin_ != nil)
1101 [origin_ release];
1102
1103 [super dealloc];
1104 }
1105
1106 - (Source *) initWithMetaIndex:(metaIndex *)index {
1107 if ((self = [super init]) != nil) {
1108 trusted_ = index->IsTrusted();
1109
1110 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
1111 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
1112 type_ = [[NSString stringWithCString:index->GetType()] retain];
1113
1114 description_ = nil;
1115 label_ = nil;
1116 origin_ = nil;
1117
1118 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1119 if (dindex != NULL) {
1120 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1121 std::string line;
1122 while (std::getline(release, line)) {
1123 std::string::size_type colon(line.find(':'));
1124 if (colon == std::string::npos)
1125 continue;
1126
1127 std::string name(line.substr(0, colon));
1128 std::string value(line.substr(colon + 1));
1129 while (!value.empty() && value[0] == ' ')
1130 value = value.substr(1);
1131
1132 if (name == "Description")
1133 description_ = [[NSString stringWithCString:value.c_str()] retain];
1134 else if (name == "Label")
1135 label_ = [[NSString stringWithCString:value.c_str()] retain];
1136 else if (name == "Origin")
1137 origin_ = [[NSString stringWithCString:value.c_str()] retain];
1138 }
1139 }
1140 } return self;
1141 }
1142
1143 - (BOOL) trusted {
1144 return trusted_;
1145 }
1146
1147 - (NSString *) uri {
1148 return uri_;
1149 }
1150
1151 - (NSString *) distribution {
1152 return distribution_;
1153 }
1154
1155 - (NSString *) type {
1156 return type_;
1157 }
1158
1159 - (NSString *) description {
1160 return description_;
1161 }
1162
1163 - (NSString *) label {
1164 return label_;
1165 }
1166
1167 - (NSString *) origin {
1168 return origin_;
1169 }
1170
1171 @end
1172 /* }}} */
1173 /* Source Cell {{{ */
1174 @interface SourceCell : UITableCell {
1175 UITextLabel *description_;
1176 UIRightTextLabel *label_;
1177 UITextLabel *origin_;
1178 }
1179
1180 - (void) dealloc;
1181
1182 - (SourceCell *) initWithSource:(Source *)source;
1183
1184 - (void) _setSelected:(float)fraction;
1185 - (void) setSelected:(BOOL)selected;
1186 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
1187 - (void) _setSelectionFadeFraction:(float)fraction;
1188
1189 @end
1190
1191 @implementation SourceCell
1192
1193 - (void) dealloc {
1194 [description_ release];
1195 [label_ release];
1196 [origin_ release];
1197 [super dealloc];
1198 }
1199
1200 - (SourceCell *) initWithSource:(Source *)source {
1201 if ((self = [super init]) != nil) {
1202 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
1203 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
1204
1205 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1206 float clear[] = {0, 0, 0, 0};
1207
1208 NSString *description = [source description];
1209 if (description == nil)
1210 description = [source uri];
1211
1212 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 270, 25)];
1213 [description_ setBackgroundColor:CGColorCreate(space, clear)];
1214 [description_ setFont:bold];
1215 [description_ setText:description];
1216
1217 NSString *label = [source label];
1218 if (label == nil)
1219 label = [source type];
1220
1221 label_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 32, 90, 25)];
1222 [label_ setBackgroundColor:CGColorCreate(space, clear)];
1223 [label_ setFont:small];
1224 [label_ setText:label];
1225
1226 NSString *origin = [source origin];
1227 if (origin == nil)
1228 origin = [source distribution];
1229
1230 origin_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
1231 [origin_ setBackgroundColor:CGColorCreate(space, clear)];
1232 [origin_ setFont:small];
1233 [origin_ setText:origin];
1234
1235 [self addSubview:description_];
1236 [self addSubview:label_];
1237 [self addSubview:origin_];
1238
1239 CFRelease(small);
1240 CFRelease(bold);
1241 } return self;
1242 }
1243
1244 - (void) _setSelected:(float)fraction {
1245 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1246
1247 float black[] = {
1248 interpolate(0.0, 1.0, fraction),
1249 interpolate(0.0, 1.0, fraction),
1250 interpolate(0.0, 1.0, fraction),
1251 1.0};
1252
1253 float blue[] = {
1254 interpolate(0.2, 1.0, fraction),
1255 interpolate(0.2, 1.0, fraction),
1256 interpolate(1.0, 1.0, fraction),
1257 1.0};
1258
1259 float gray[] = {
1260 interpolate(0.4, 1.0, fraction),
1261 interpolate(0.4, 1.0, fraction),
1262 interpolate(0.4, 1.0, fraction),
1263 1.0};
1264
1265 [description_ setColor:CGColorCreate(space, black)];
1266 [label_ setColor:CGColorCreate(space, blue)];
1267 [origin_ setColor:CGColorCreate(space, gray)];
1268 }
1269
1270 - (void) setSelected:(BOOL)selected {
1271 [self _setSelected:(selected ? 1.0 : 0.0)];
1272 [super setSelected:selected];
1273 }
1274
1275 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
1276 if (!fade)
1277 [self _setSelected:(selected ? 1.0 : 0.0)];
1278 [super setSelected:selected withFade:fade];
1279 }
1280
1281 - (void) _setSelectionFadeFraction:(float)fraction {
1282 [self _setSelected:fraction];
1283 [super _setSelectionFadeFraction:fraction];
1284 }
1285
1286 @end
1287 /* }}} */
1288 /* Sources View {{{ */
1289 @interface SourcesView : ResetView {
1290 UISectionList *list_;
1291 Database *database_;
1292 id delegate_;
1293 NSMutableArray *sources_;
1294 UIAlertSheet *alert_;
1295 }
1296
1297 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
1298 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
1299 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
1300
1301 - (int) numberOfRowsInTable:(UITable *)table;
1302 - (float) table:(UITable *)table heightForRow:(int)row;
1303 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col;
1304 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
1305 - (void) tableRowSelected:(NSNotification*)notification;
1306
1307 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
1308
1309 - (void) dealloc;
1310 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
1311 - (void) setDelegate:(id)delegate;
1312 - (void) reloadData;
1313 - (NSString *) leftTitle;
1314 - (NSString *) rightTitle;
1315 @end
1316
1317 @implementation SourcesView
1318
1319 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
1320 return 1;
1321 }
1322
1323 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
1324 return @"sources";
1325 }
1326
1327 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
1328 return 0;
1329 }
1330
1331 - (int) numberOfRowsInTable:(UITable *)table {
1332 return [sources_ count];
1333 }
1334
1335 - (float) table:(UITable *)table heightForRow:(int)row {
1336 return 64;
1337 }
1338
1339 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
1340 return [[[SourceCell alloc] initWithSource:[sources_ objectAtIndex:row]] autorelease];
1341 }
1342
1343 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
1344 return NO;
1345 }
1346
1347 - (void) tableRowSelected:(NSNotification*)notification {
1348 UITable *table([list_ table]);
1349 int row([table selectedRow]);
1350 if (row == INT_MAX)
1351 return;
1352
1353 [table selectRow:-1 byExtendingSelection:NO withFade:YES];
1354 }
1355
1356 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1357 [alert_ dismiss];
1358 [alert_ release];
1359 alert_ = nil;
1360 }
1361
1362 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1363 switch (button) {
1364 case 0:
1365 alert_ = [[UIAlertSheet alloc]
1366 initWithTitle:@"Unimplemented"
1367 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1368 defaultButtonIndex:0
1369 delegate:self
1370 context:self
1371 ];
1372
1373 [alert_ setBodyText:@"This feature will be implemented soon. In the mean time, you may add sources by adding .list files to '/etc/apt/sources.list.d'. If you'd like to be in the default list, please contact the author of Packager."];
1374 [alert_ popupAlertAnimated:YES];
1375 break;
1376
1377 case 1:
1378 [delegate_ update];
1379 break;
1380 }
1381 }
1382
1383 - (void) dealloc {
1384 if (sources_ != nil)
1385 [sources_ release];
1386 [list_ release];
1387 [super dealloc];
1388 }
1389
1390 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
1391 if ((self = [super initWithFrame:frame]) != nil) {
1392 database_ = database;
1393 sources_ = nil;
1394
1395 CGSize navsize = [UINavigationBar defaultSize];
1396 CGRect navrect = {{0, 0}, navsize};
1397 CGRect bounds = [self bounds];
1398
1399 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1400 [self addSubview:navbar_];
1401
1402 [navbar_ setBarStyle:1];
1403 [navbar_ setDelegate:self];
1404
1405 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Sources"] autorelease];
1406 [navbar_ pushNavigationItem:navitem];
1407
1408 list_ = [[UISectionList alloc] initWithFrame:CGRectMake(
1409 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
1410 )];
1411
1412 [self addSubview:list_];
1413
1414 [list_ setDataSource:self];
1415 [list_ setShouldHideHeaderInShortLists:NO];
1416
1417 UITableColumn *column = [[UITableColumn alloc]
1418 initWithTitle:@"Name"
1419 identifier:@"name"
1420 width:frame.size.width
1421 ];
1422
1423 UITable *table = [list_ table];
1424 [table setSeparatorStyle:1];
1425 [table addTableColumn:column];
1426 [table setDelegate:self];
1427 } return self;
1428 }
1429
1430 - (void) setDelegate:(id)delegate {
1431 delegate_ = delegate;
1432 }
1433
1434 - (void) reloadData {
1435 pkgSourceList list;
1436 _assert(list.ReadMainList());
1437
1438 if (sources_ != nil)
1439 [sources_ release];
1440
1441 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
1442 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1443 [sources_ addObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]];
1444
1445 [self resetView];
1446 [list_ reloadData];
1447 }
1448
1449 - (NSString *) leftTitle {
1450 return @"Refresh All";
1451 }
1452
1453 - (NSString *) rightTitle {
1454 return @"Edit";
1455 }
1456
1457 @end
1458 /* }}} */
1459
1460 @implementation Database
1461
1462 - (void) _readStatus:(NSNumber *)fd {
1463 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1464
1465 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1466 std::istream is(&ib);
1467 std::string line;
1468
1469 const char *error;
1470 int offset;
1471 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
1472
1473 pcre_extra *study = NULL;
1474 int capture;
1475 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1476 int matches[(capture + 1) * 3];
1477
1478 while (std::getline(is, line)) {
1479 const char *data(line.c_str());
1480
1481 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
1482
1483 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1484 float percent;
1485 buffer >> percent;
1486 [delegate_ setPercent:(percent / 100)];
1487
1488 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1489 std::string type(line.substr(matches[2], matches[3] - matches[2]));
1490
1491 if (type == "pmerror")
1492 [delegate_ setError:string];
1493 else if (type == "pmstatus")
1494 [delegate_ setTitle:string];
1495 else if (type == "pmconffile")
1496 ;
1497 else _assert(false);
1498 }
1499
1500 [pool release];
1501 }
1502
1503 - (void) _readOutput:(NSNumber *)fd {
1504 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1505
1506 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1507 std::istream is(&ib);
1508 std::string line;
1509
1510 while (std::getline(is, line))
1511 [delegate_ addOutput:[NSString stringWithCString:line.c_str()]];
1512
1513 [pool release];
1514 }
1515
1516 - (Package *) packageWithName:(NSString *)name {
1517 pkgCache::PkgIterator iterator(cache_->FindPkg([name cString]));
1518 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1519 }
1520
1521 - (Database *) init {
1522 if ((self = [super init]) != nil) {
1523 records_ = NULL;
1524 resolver_ = NULL;
1525 fetcher_ = NULL;
1526 lock_ = NULL;
1527
1528 int fds[2];
1529
1530 _assert(pipe(fds) != -1);
1531 statusfd_ = fds[1];
1532
1533 [NSThread
1534 detachNewThreadSelector:@selector(_readStatus:)
1535 toTarget:self
1536 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1537 ];
1538
1539 _assert(pipe(fds) != -1);
1540 _assert(dup2(fds[1], 1) != -1);
1541 _assert(close(fds[1]) != -1);
1542
1543 [NSThread
1544 detachNewThreadSelector:@selector(_readOutput:)
1545 toTarget:self
1546 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1547 ];
1548 } return self;
1549 }
1550
1551 - (pkgCacheFile &) cache {
1552 return cache_;
1553 }
1554
1555 - (pkgRecords *) records {
1556 return records_;
1557 }
1558
1559 - (pkgProblemResolver *) resolver {
1560 return resolver_;
1561 }
1562
1563 - (pkgAcquire &) fetcher {
1564 return *fetcher_;
1565 }
1566
1567 - (void) reloadData {
1568 _error->Discard();
1569 manager_ = NULL;
1570 delete lock_;
1571 delete fetcher_;
1572 delete resolver_;
1573 delete records_;
1574 cache_.Close();
1575 cache_.Open(progress_, true);
1576 records_ = new pkgRecords(cache_);
1577 resolver_ = new pkgProblemResolver(cache_);
1578 fetcher_ = new pkgAcquire(&status_);
1579 lock_ = NULL;
1580 }
1581
1582 - (void) prepare {
1583 pkgRecords records(cache_);
1584
1585 lock_ = new FileFd();
1586 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1587 _assert(!_error->PendingError());
1588
1589 pkgSourceList list;
1590 _assert(list.ReadMainList());
1591
1592 manager_ = (_system->CreatePM(cache_));
1593 _assert(manager_->GetArchives(fetcher_, &list, &records));
1594 _assert(!_error->PendingError());
1595 }
1596
1597 - (void) perform {
1598 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1599 return;
1600
1601 _system->UnLock();
1602 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1603
1604 if (result == pkgPackageManager::Failed)
1605 return;
1606 if (_error->PendingError())
1607 return;
1608 if (result != pkgPackageManager::Completed)
1609 return;
1610 }
1611
1612 - (void) update {
1613 pkgSourceList list;
1614 _assert(list.ReadMainList());
1615
1616 FileFd lock;
1617 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1618 _assert(!_error->PendingError());
1619
1620 pkgAcquire fetcher(&status_);
1621 _assert(list.GetIndexes(&fetcher));
1622 _assert(fetcher.Run(PulseInterval_) != pkgAcquire::Failed);
1623
1624 bool failed = false;
1625 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1626 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1627 (*item)->Finished();
1628 failed = true;
1629 }
1630
1631 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1632 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1633 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1634 }
1635 }
1636
1637 - (void) upgrade {
1638 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1639 _assert(pkgApplyStatus(cache_));
1640
1641 if (cache_->BrokenCount() != 0) {
1642 _assert(pkgFixBroken(cache_));
1643 _assert(cache_->BrokenCount() == 0);
1644 _assert(pkgMinimizeUpgrade(cache_));
1645 }
1646
1647 _assert(pkgDistUpgrade(cache_));
1648 }
1649
1650 - (void) setDelegate:(id)delegate {
1651 delegate_ = delegate;
1652 status_.setDelegate(delegate);
1653 progress_.setDelegate(delegate);
1654 }
1655
1656 @end
1657
1658 /* Progress Data {{{ */
1659 @interface ProgressData : NSObject {
1660 SEL selector_;
1661 id target_;
1662 id object_;
1663 }
1664
1665 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
1666
1667 - (SEL) selector;
1668 - (id) target;
1669 - (id) object;
1670 @end
1671
1672 @implementation ProgressData
1673
1674 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
1675 if ((self = [super init]) != nil) {
1676 selector_ = selector;
1677 target_ = target;
1678 object_ = object;
1679 } return self;
1680 }
1681
1682 - (SEL) selector {
1683 return selector_;
1684 }
1685
1686 - (id) target {
1687 return target_;
1688 }
1689
1690 - (id) object {
1691 return object_;
1692 }
1693
1694 @end
1695 /* }}} */
1696 /* Progress View {{{ */
1697 @interface ProgressView : UIView <
1698 ProgressDelegate
1699 > {
1700 UIView *view_;
1701 UIView *background_;
1702 UITransitionView *transition_;
1703 UIView *overlay_;
1704 UINavigationBar *navbar_;
1705 UIProgressBar *progress_;
1706 UITextView *output_;
1707 UITextLabel *status_;
1708 id delegate_;
1709 UIAlertSheet *alert_;
1710 }
1711
1712 - (void) dealloc;
1713
1714 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
1715 - (void) setContentView:(UIView *)view;
1716 - (void) resetView;
1717
1718 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
1719
1720 - (void) _retachThread;
1721 - (void) _detachNewThreadData:(ProgressData *)data;
1722 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object;
1723
1724 - (void) setError:(NSString *)error;
1725 - (void) _setError:(NSString *)error;
1726
1727 - (void) setTitle:(NSString *)title;
1728 - (void) _setTitle:(NSString *)title;
1729
1730 - (void) setPercent:(float)percent;
1731 - (void) _setPercent:(NSNumber *)percent;
1732
1733 - (void) addOutput:(NSString *)output;
1734 - (void) _addOutput:(NSString *)output;
1735
1736 - (void) setStatusFail;
1737 @end
1738
1739 @protocol ProgressViewDelegate
1740 - (void) progressViewIsComplete:(ProgressView *)sender;
1741 @end
1742
1743 @implementation ProgressView
1744
1745 - (void) dealloc {
1746 [view_ release];
1747 [background_ release];
1748 [transition_ release];
1749 [overlay_ release];
1750 [navbar_ release];
1751 [progress_ release];
1752 [output_ release];
1753 [status_ release];
1754 [super dealloc];
1755 }
1756
1757 - (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
1758 if ((self = [super initWithFrame:frame]) != nil) {
1759 delegate_ = delegate;
1760 alert_ = nil;
1761
1762 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
1763 float black[] = {0.0, 0.0, 0.0, 1.0};
1764 float white[] = {1.0, 1.0, 1.0, 1.0};
1765 float clear[] = {0.0, 0.0, 0.0, 0.0};
1766
1767 background_ = [[UIView alloc] initWithFrame:[self bounds]];
1768 [background_ setBackgroundColor:CGColorCreate(space, black)];
1769 [self addSubview:background_];
1770
1771 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1772 [self addSubview:transition_];
1773
1774 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
1775
1776 CGSize navsize = [UINavigationBar defaultSize];
1777 CGRect navrect = {{0, 0}, navsize};
1778
1779 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1780 [overlay_ addSubview:navbar_];
1781
1782 [navbar_ setBarStyle:1];
1783 [navbar_ setDelegate:self];
1784
1785 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Running..."] autorelease];
1786 [navbar_ pushNavigationItem:navitem];
1787
1788 CGRect bounds = [overlay_ bounds];
1789 CGSize prgsize = [UIProgressBar defaultSize];
1790
1791 CGRect prgrect = {{
1792 (bounds.size.width - prgsize.width) / 2,
1793 bounds.size.height - prgsize.height - 20
1794 }, prgsize};
1795
1796 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
1797 [overlay_ addSubview:progress_];
1798
1799 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
1800 10,
1801 bounds.size.height - prgsize.height - 50,
1802 bounds.size.width - 20,
1803 24
1804 )];
1805
1806 [status_ setColor:CGColorCreate(space, white)];
1807 [status_ setBackgroundColor:CGColorCreate(space, clear)];
1808
1809 [status_ setCentersHorizontally:YES];
1810 //[status_ setFont:font];
1811
1812 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
1813 10,
1814 navrect.size.height + 20,
1815 bounds.size.width - 20,
1816 bounds.size.height - navsize.height - 62 - navrect.size.height
1817 )];
1818
1819 //[output_ setTextFont:@"Courier New"];
1820 [output_ setTextSize:12];
1821
1822 [output_ setTextColor:CGColorCreate(space, white)];
1823 [output_ setBackgroundColor:CGColorCreate(space, clear)];
1824
1825 [output_ setMarginTop:0];
1826 [output_ setAllowsRubberBanding:YES];
1827
1828 [overlay_ addSubview:output_];
1829 [overlay_ addSubview:status_];
1830
1831 [progress_ setStyle:0];
1832 } return self;
1833 }
1834
1835 - (void) setContentView:(UIView *)view {
1836 view_ = view;
1837 }
1838
1839 - (void) resetView {
1840 [transition_ transition:6 toView:view_];
1841 }
1842
1843 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1844 [alert_ dismiss];
1845 [alert_ release];
1846 alert_ = nil;
1847 }
1848
1849 - (void) _retachThread {
1850 [delegate_ progressViewIsComplete:self];
1851 [self resetView];
1852 }
1853
1854 - (void) _detachNewThreadData:(ProgressData *)data {
1855 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1856
1857 [[data target] performSelector:[data selector] withObject:[data object]];
1858 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
1859
1860 [data release];
1861 [pool release];
1862 }
1863
1864 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object {
1865 [status_ setText:nil];
1866 [output_ setText:@""];
1867 [progress_ setProgress:0];
1868
1869 [transition_ transition:6 toView:overlay_];
1870
1871 [NSThread
1872 detachNewThreadSelector:@selector(_detachNewThreadData:)
1873 toTarget:self
1874 withObject:[[ProgressData alloc]
1875 initWithSelector:selector
1876 target:target
1877 object:object
1878 ]
1879 ];
1880 }
1881
1882 - (void) setStatusFail {
1883 }
1884
1885 - (void) setError:(NSString *)error {
1886 [self
1887 performSelectorOnMainThread:@selector(_setError:)
1888 withObject:error
1889 waitUntilDone:YES
1890 ];
1891 }
1892
1893 - (void) _setError:(NSString *)error {
1894 _assert(alert_ == nil);
1895
1896 alert_ = [[UIAlertSheet alloc]
1897 initWithTitle:@"Package Error"
1898 buttons:[NSArray arrayWithObjects:@"Okay", nil]
1899 defaultButtonIndex:0
1900 delegate:self
1901 context:self
1902 ];
1903
1904 [alert_ setBodyText:error];
1905 [alert_ popupAlertAnimated:YES];
1906 }
1907
1908 - (void) setTitle:(NSString *)title {
1909 [self
1910 performSelectorOnMainThread:@selector(_setTitle:)
1911 withObject:title
1912 waitUntilDone:YES
1913 ];
1914 }
1915
1916 - (void) _setTitle:(NSString *)title {
1917 [status_ setText:[title stringByAppendingString:@"..."]];
1918 }
1919
1920 - (void) setPercent:(float)percent {
1921 [self
1922 performSelectorOnMainThread:@selector(_setPercent:)
1923 withObject:[NSNumber numberWithFloat:percent]
1924 waitUntilDone:YES
1925 ];
1926 }
1927
1928 - (void) _setPercent:(NSNumber *)percent {
1929 [progress_ setProgress:[percent floatValue]];
1930 }
1931
1932 - (void) addOutput:(NSString *)output {
1933 [self
1934 performSelectorOnMainThread:@selector(_addOutput:)
1935 withObject:output
1936 waitUntilDone:YES
1937 ];
1938 }
1939
1940 - (void) _addOutput:(NSString *)output {
1941 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
1942 CGSize size = [output_ contentSize];
1943 CGRect rect = {{0, size.height}, {size.width, 0}};
1944 [output_ scrollRectToVisible:rect animated:YES];
1945 }
1946
1947 @end
1948 /* }}} */
1949
1950 @protocol PackagesViewDelegate
1951 - (void) perform;
1952 - (void) update;
1953 - (void) openURL:(NSString *)url;
1954 @end
1955
1956 /* PackagesView {{{ */
1957 @interface PackagesView : ResetView <
1958 PackageCellDelegate
1959 > {
1960 Database *database_;
1961 NSMutableArray *packages_;
1962 NSMutableArray *sections_;
1963 id delegate_;
1964 UISectionList *list_;
1965 UITransitionView *transition_;
1966 Package *package_;
1967 NSString *pkgname_;
1968 PackageView *pkgview_;
1969 }
1970
1971 - (int) numberOfSectionsInSectionList:(UISectionList *)list;
1972 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section;
1973 - (int) sectionList:(UISectionList *)list rowForSection:(int)section;
1974
1975 - (int) numberOfRowsInTable:(UITable *)table;
1976 - (float) table:(UITable *)table heightForRow:(int)row;
1977 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col;
1978 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row;
1979 - (void) tableRowSelected:(NSNotification*)notification;
1980
1981 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
1982 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item;
1983
1984 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database;
1985 - (void) setDelegate:(id)delegate;
1986 - (void) deselect;
1987 - (void) reloadData:(BOOL)reset;
1988
1989 - (NSMutableArray *) packages;
1990 - (NSString *) title;
1991 - (void) perform:(Package *)package;
1992 - (void) addPackage:(Package *)package;
1993 - (NSString *) versionWithPackage:(Package *)package;
1994 @end
1995
1996 @implementation PackagesView
1997
1998 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
1999 return [sections_ count];
2000 }
2001
2002 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2003 return [[sections_ objectAtIndex:section] name];
2004 }
2005
2006 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2007 return [[sections_ objectAtIndex:section] row];
2008 }
2009
2010 - (int) numberOfRowsInTable:(UITable *)table {
2011 return [packages_ count];
2012 }
2013
2014 - (float) table:(UITable *)table heightForRow:(int)row {
2015 return 64;
2016 }
2017
2018 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
2019 return [[[PackageCell alloc] initWithPackage:[packages_ objectAtIndex:row] delegate:self] autorelease];
2020 }
2021
2022 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2023 return YES;
2024 }
2025
2026 - (void) tableRowSelected:(NSNotification*)notification {
2027 int row = [[list_ table] selectedRow];
2028 if (row == INT_MAX)
2029 return;
2030
2031 package_ = [packages_ objectAtIndex:row];
2032 pkgname_ = [[package_ name] retain];
2033
2034 UINavigationItem *navitem = [[UINavigationItem alloc] initWithTitle:[package_ name]];
2035 [navbar_ pushNavigationItem:navitem];
2036
2037 [navbar_ showButtonsWithLeftTitle:nil rightTitle:[self title]];
2038
2039 [pkgview_ setPackage:package_];
2040 [transition_ transition:1 toView:pkgview_];
2041 }
2042
2043 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2044 if (button == 0) {
2045 [self perform:package_];
2046
2047 pkgProblemResolver *resolver = [database_ resolver];
2048
2049 resolver->InstallProtect();
2050 if (!resolver->Resolve(true))
2051 _error->Discard();
2052
2053 [delegate_ perform];
2054 }
2055 }
2056
2057 - (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
2058 [self deselect];
2059 [super navigationBar:navbar poppedItem:item];
2060 }
2061
2062 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database {
2063 if ((self = [super initWithFrame:frame]) != nil) {
2064 database_ = [database retain];
2065
2066 struct CGRect bounds = [self bounds];
2067 CGSize navsize = [UINavigationBar defaultSize];
2068 CGRect navrect = {{0, 0}, navsize};
2069
2070 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2071 [self addSubview:navbar_];
2072
2073 [navbar_ setBarStyle:1];
2074 [navbar_ setDelegate:self];
2075
2076 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:[self title]] autorelease];
2077 [navbar_ pushNavigationItem:navitem];
2078 [navitem setBackButtonTitle:@"Packages"];
2079
2080 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
2081 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
2082 )];
2083
2084 [self addSubview:transition_];
2085
2086 list_ = [[UISectionList alloc] initWithFrame:[transition_ bounds] showSectionIndex:NO];
2087 [list_ setDataSource:self];
2088 [list_ setShouldHideHeaderInShortLists:NO];
2089
2090 [transition_ transition:0 toView:list_];
2091
2092 UITableColumn *column = [[UITableColumn alloc]
2093 initWithTitle:@"Name"
2094 identifier:@"name"
2095 width:frame.size.width
2096 ];
2097
2098 UITable *table = [list_ table];
2099 [table setSeparatorStyle:1];
2100 [table addTableColumn:column];
2101 [table setDelegate:self];
2102
2103 pkgview_ = [[PackageView alloc] initWithFrame:[transition_ bounds] database:database_];
2104 } return self;
2105 }
2106
2107 - (void) setDelegate:(id)delegate {
2108 delegate_ = delegate;
2109 [pkgview_ setDelegate:delegate];
2110 }
2111
2112 - (void) deselect {
2113 [transition_ transition:(resetting_ ? 0 : 2) toView:list_];
2114 UITable *table = [list_ table];
2115 [table selectRow:-1 byExtendingSelection:NO withFade:(resetting_ ? NO : YES)];
2116 package_ = nil;
2117 }
2118
2119 - (void) reloadData:(BOOL)reset {
2120 if (sections_ != nil)
2121 [sections_ release];
2122 if (packages_ != nil)
2123 [packages_ release];
2124
2125 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2126
2127 for (pkgCache::PkgIterator iterator = [database_ cache]->PkgBegin(); !iterator.end(); ++iterator)
2128 if (Package *package = [Package packageWithIterator:iterator database:database_])
2129 [self addPackage:package];
2130
2131 [packages_ sortUsingSelector:@selector(compareBySectionAndName:)];
2132 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2133
2134 Section *section = nil;
2135 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
2136 Package *package = [packages_ objectAtIndex:offset];
2137 NSString *name = [package section];
2138
2139 if (section == nil || ![[section name] isEqual:name]) {
2140 section = [[Section alloc] initWithName:name row:offset];
2141 [sections_ addObject:section];
2142 }
2143
2144 [section addPackage:package];
2145 }
2146
2147 [list_ reloadData];
2148 if (reset)
2149 [self resetView];
2150 else if (package_ != nil) {
2151 package_ = [database_ packageWithName:pkgname_];
2152 [pkgview_ setPackage:package_];
2153 }
2154 }
2155
2156 - (NSMutableArray *) packages {
2157 return packages_;
2158 }
2159
2160 - (NSString *) title {
2161 return nil;
2162 }
2163
2164 - (void) perform:(Package *)package {
2165 }
2166
2167 - (void) addPackage:(Package *)package {
2168 [packages_ addObject:package];
2169 }
2170
2171 - (NSString *) versionWithPackage:(Package *)package {
2172 return nil;
2173 }
2174
2175 @end
2176 /* }}} */
2177
2178 /* InstallView {{{ */
2179 @interface InstallView : PackagesView {
2180 }
2181
2182 - (NSString *) title;
2183 - (void) addPackage:(Package *)package;
2184 - (void) perform:(Package *)package;
2185 - (NSString *) versionWithPackage:(Package *)package;
2186 @end
2187
2188 @implementation InstallView
2189
2190 - (NSString *) title {
2191 return @"Install";
2192 }
2193
2194 - (void) addPackage:(Package *)package {
2195 if ([package installed] == nil)
2196 [super addPackage:package];
2197 }
2198
2199 - (void) perform:(Package *)package {
2200 [package install];
2201 }
2202
2203 - (NSString *) versionWithPackage:(Package *)package {
2204 return [package latest];
2205 }
2206
2207 @end
2208 /* }}} */
2209 /* UpgradeView {{{ */
2210 @interface UpgradeView : PackagesView {
2211 }
2212
2213 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2214
2215 - (NSString *) title;
2216 - (NSString *) leftTitle;
2217 - (void) addPackage:(Package *)package;
2218 - (void) perform:(Package *)package;
2219 - (NSString *) versionWithPackage:(Package *)package;
2220 @end
2221
2222 @implementation UpgradeView
2223
2224 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2225 if (button != 1)
2226 [super navigationBar:navbar buttonClicked:button];
2227 else {
2228 [database_ upgrade];
2229 [delegate_ perform];
2230 }
2231 }
2232
2233 - (NSString *) title {
2234 return @"Upgrade";
2235 }
2236
2237 - (NSString *) leftTitle {
2238 return [packages_ count] == 0 ? nil : @"Upgrade All";
2239 }
2240
2241 - (void) addPackage:(Package *)package {
2242 NSString *installed = [package installed];
2243 if (installed != nil && [[package latest] compare:installed] != NSOrderedSame)
2244 [super addPackage:package];
2245 }
2246
2247 - (void) perform:(Package *)package {
2248 [package install];
2249 }
2250
2251 - (NSString *) versionWithPackage:(Package *)package {
2252 return [package latest];
2253 }
2254
2255 @end
2256 /* }}} */
2257 /* UninstallView {{{ */
2258 @interface UninstallView : PackagesView {
2259 }
2260
2261 - (NSString *) title;
2262 - (void) addPackage:(Package *)package;
2263 - (void) perform:(Package *)package;
2264 - (NSString *) versionWithPackage:(Package *)package;
2265 @end
2266
2267 @implementation UninstallView
2268
2269 - (NSString *) title {
2270 return @"Uninstall";
2271 }
2272
2273 - (void) addPackage:(Package *)package {
2274 if ([package installed] != nil)
2275 [super addPackage:package];
2276 }
2277
2278 - (void) perform:(Package *)package {
2279 [package remove];
2280 }
2281
2282 - (NSString *) versionWithPackage:(Package *)package {
2283 return [package installed];
2284 }
2285
2286 @end
2287 /* }}} */
2288
2289 @interface Cydia : UIApplication <
2290 ConfirmationViewDelegate,
2291 PackagesViewDelegate,
2292 ProgressViewDelegate
2293 > {
2294 UIWindow *window_;
2295 UIView *underlay_;
2296 UIView *overlay_;
2297 UITransitionView *transition_;
2298 UIButtonBar *buttonbar_;
2299
2300 UIAlertSheet *alert_;
2301 ConfirmationView *confirm_;
2302
2303 Database *database_;
2304 ProgressView *progress_;
2305
2306 UIView *featured_;
2307 UINavigationBar *navbar_;
2308 UIScroller *scroller_;
2309 UIWebView *webview_;
2310 NSURL *url_;
2311 UIProgressIndicator *indicator_;
2312
2313 InstallView *install_;
2314 UpgradeView *upgrade_;
2315 UninstallView *uninstall_;
2316 SourcesView *sources_;
2317 }
2318
2319 - (void) loadNews;
2320 - (void) reloadData:(BOOL)reset;
2321 - (void) perform;
2322 - (void) cancel;
2323 - (void) confirm;
2324 - (void) update;
2325
2326 - (void) progressViewIsComplete:(ProgressView *)progress;
2327
2328 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button;
2329 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button;
2330 - (void) buttonBarItemTapped:(id)sender;
2331
2332 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old;
2333
2334 - (void) applicationDidFinishLaunching:(id)unused;
2335 @end
2336
2337 #include <objc/objc-class.h>
2338
2339 @implementation Cydia
2340
2341 - (void) loadNews {
2342 NSMutableURLRequest *request = [NSMutableURLRequest
2343 requestWithURL:url_
2344 cachePolicy:NSURLRequestReloadIgnoringCacheData
2345 timeoutInterval:30.0
2346 ];
2347
2348 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
2349 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
2350
2351 [webview_ loadRequest:request];
2352 [indicator_ startAnimation];
2353 }
2354
2355 - (void) reloadData:(BOOL)reset {
2356 [database_ reloadData];
2357 [install_ reloadData:reset];
2358 [upgrade_ reloadData:reset];
2359 [uninstall_ reloadData:reset];
2360 [sources_ reloadData];
2361
2362 if (size_t count = [[upgrade_ packages] count]) {
2363 NSString *badge([[NSNumber numberWithInt:count] stringValue]);
2364 [buttonbar_ setBadgeValue:badge forButton:3];
2365 [buttonbar_ setBadgeAnimated:YES forButton:3];
2366 [self setApplicationBadge:badge];
2367 } else {
2368 [buttonbar_ setBadgeValue:nil forButton:3];
2369 [buttonbar_ setBadgeAnimated:NO forButton:3];
2370 [self removeApplicationBadge];
2371 }
2372 }
2373
2374 - (void) perform {
2375 [database_ prepare];
2376 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
2377 }
2378
2379 - (void) cancel {
2380 [self reloadData:NO];
2381 [confirm_ release];
2382 confirm_ = nil;
2383 }
2384
2385 - (void) confirm {
2386 [overlay_ removeFromSuperview];
2387
2388 [progress_
2389 detachNewThreadSelector:@selector(perform)
2390 toTarget:database_
2391 withObject:nil
2392 ];
2393 }
2394
2395 - (void) update {
2396 [progress_
2397 detachNewThreadSelector:@selector(update)
2398 toTarget:database_
2399 withObject:nil
2400 ];
2401 }
2402
2403 - (void) progressViewIsComplete:(ProgressView *)progress {
2404 [self reloadData:YES];
2405
2406 if (confirm_ != nil) {
2407 [underlay_ addSubview:overlay_];
2408 [confirm_ removeFromSuperview];
2409 [confirm_ release];
2410 confirm_ = nil;
2411 }
2412 }
2413
2414 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
2415 switch (button) {
2416 case 0:
2417 [self loadNews];
2418 break;
2419
2420 case 1:
2421 _assert(alert_ == nil);
2422
2423 alert_ = [[UIAlertSheet alloc]
2424 initWithTitle:@"About Cydia Packager"
2425 buttons:[NSArray arrayWithObjects:@"Close", nil]
2426 defaultButtonIndex:0
2427 delegate:self
2428 context:self
2429 ];
2430
2431 [alert_ setBodyText:
2432 @"Copyright (C) 2007\n"
2433 "Jay Freeman (saurik)\n"
2434 "saurik@saurik.com\n"
2435 "http://www.saurik.com/\n"
2436 "\n"
2437 "The Okori Group\n"
2438 "http://www.theokorigroup.com/\n"
2439 "\n"
2440 "College of Creative Studies,\n"
2441 "University of California,\n"
2442 "Santa Barbara\n"
2443 "http://www.ccs.ucsb.edu/\n"
2444 "\n"
2445 "Special Thanks:\n"
2446 "bad_, BHSPitMonkey, Cobra, core,\n"
2447 "Corona, cromas, Darken, dtzWill,\n"
2448 "francis, Godores, jerry, Kingstone,\n"
2449 "lounger, rockabilly, tman, Wbiggs"
2450 ];
2451
2452 [alert_ presentSheetFromButtonBar:buttonbar_];
2453 break;
2454 }
2455 }
2456
2457 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2458 [alert_ dismiss];
2459 [alert_ release];
2460 alert_ = nil;
2461 }
2462
2463 - (void) buttonBarItemTapped:(id)sender {
2464 UIView *view;
2465
2466 switch ([sender tag]) {
2467 case 1: view = featured_; break;
2468 case 2: view = install_; break;
2469 case 3: view = upgrade_; break;
2470 case 4: view = uninstall_; break;
2471 case 5: view = sources_; break;
2472
2473 default:
2474 _assert(false);
2475 }
2476
2477 if ([view respondsToSelector:@selector(resetView)])
2478 [(id) view resetView];
2479 [transition_ transition:0 toView:view];
2480 }
2481
2482 - (void) view:(UIView *)view didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
2483 [scroller_ setContentSize:frame.size];
2484 [indicator_ stopAnimation];
2485 }
2486
2487 - (void) applicationDidFinishLaunching:(id)unused {
2488 _assert(pkgInitConfig(*_config));
2489 _assert(pkgInitSystem(*_config, _system));
2490
2491 alert_ = nil;
2492 confirm_ = nil;
2493
2494 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
2495 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
2496
2497 [window_ orderFront: self];
2498 [window_ makeKey: self];
2499 [window_ _setHidden: NO];
2500
2501 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
2502 [window_ setContentView:progress_];
2503
2504 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
2505 [progress_ setContentView:underlay_];
2506
2507 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
2508 [underlay_ addSubview:overlay_];
2509
2510 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
2511 0, 0, screenrect.size.width, screenrect.size.height - 48
2512 )];
2513
2514 [overlay_ addSubview:transition_];
2515
2516 featured_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2517
2518 CGSize navsize = [UINavigationBar defaultSize];
2519 CGRect navrect = {{0, 0}, navsize};
2520
2521 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2522 [featured_ addSubview:navbar_];
2523
2524 [navbar_ setBarStyle:1];
2525 [navbar_ setDelegate:self];
2526
2527 [navbar_ showButtonsWithLeftTitle:@"About" rightTitle:@"Reload"];
2528
2529 UINavigationItem *navitem = [[UINavigationItem alloc] initWithTitle:@"Featured"];
2530 [navbar_ pushNavigationItem:navitem];
2531
2532 struct CGRect subbounds = [featured_ bounds];
2533 subbounds.origin.y += navsize.height;
2534 subbounds.size.height -= navsize.height;
2535
2536 UIImageView *pinstripe = [[UIImageView alloc] initWithFrame:subbounds];
2537 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
2538 [featured_ addSubview:pinstripe];
2539
2540 scroller_ = [[UIScroller alloc] initWithFrame:subbounds];
2541 [featured_ addSubview:scroller_];
2542
2543 [scroller_ setScrollingEnabled:YES];
2544 [scroller_ setAdjustForContentSizeChange:YES];
2545 [scroller_ setClipsSubviews:YES];
2546 [scroller_ setAllowsRubberBanding:YES];
2547 [scroller_ setScrollDecelerationFactor:0.99];
2548 [scroller_ setDelegate:self];
2549
2550 webview_ = [[UIWebView alloc] initWithFrame:[scroller_ bounds]];
2551 [scroller_ addSubview:webview_];
2552
2553 [webview_ setTilingEnabled:YES];
2554 [webview_ setTileSize:CGSizeMake(screenrect.size.width, 500)];
2555 [webview_ setAutoresizes:YES];
2556 [webview_ setDelegate:self];
2557
2558 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
2559 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(87, 15, indsize.width, indsize.height)];
2560 [indicator_ setStyle:2];
2561 [featured_ addSubview:indicator_];
2562
2563 NSArray *buttonitems = [NSArray arrayWithObjects:
2564 [NSDictionary dictionaryWithObjectsAndKeys:
2565 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
2566 @"featured-up.png", kUIButtonBarButtonInfo,
2567 @"featured-dn.png", kUIButtonBarButtonSelectedInfo,
2568 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
2569 self, kUIButtonBarButtonTarget,
2570 @"Featured", kUIButtonBarButtonTitle,
2571 @"0", kUIButtonBarButtonType,
2572 nil],
2573
2574 [NSDictionary dictionaryWithObjectsAndKeys:
2575 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
2576 @"install-up.png", kUIButtonBarButtonInfo,
2577 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
2578 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
2579 self, kUIButtonBarButtonTarget,
2580 @"Install", kUIButtonBarButtonTitle,
2581 @"0", kUIButtonBarButtonType,
2582 nil],
2583
2584 [NSDictionary dictionaryWithObjectsAndKeys:
2585 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
2586 @"upgrade-up.png", kUIButtonBarButtonInfo,
2587 @"upgrade-dn.png", kUIButtonBarButtonSelectedInfo,
2588 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
2589 self, kUIButtonBarButtonTarget,
2590 @"Upgrade", kUIButtonBarButtonTitle,
2591 @"0", kUIButtonBarButtonType,
2592 nil],
2593
2594 [NSDictionary dictionaryWithObjectsAndKeys:
2595 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
2596 @"uninstall-up.png", kUIButtonBarButtonInfo,
2597 @"uninstall-dn.png", kUIButtonBarButtonSelectedInfo,
2598 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
2599 self, kUIButtonBarButtonTarget,
2600 @"Uninstall", kUIButtonBarButtonTitle,
2601 @"0", kUIButtonBarButtonType,
2602 nil],
2603
2604 [NSDictionary dictionaryWithObjectsAndKeys:
2605 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
2606 @"sources-up.png", kUIButtonBarButtonInfo,
2607 @"sources-dn.png", kUIButtonBarButtonSelectedInfo,
2608 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
2609 self, kUIButtonBarButtonTarget,
2610 @"Sources", kUIButtonBarButtonTitle,
2611 @"0", kUIButtonBarButtonType,
2612 nil],
2613 nil];
2614
2615 buttonbar_ = [[UIButtonBar alloc]
2616 initInView:overlay_
2617 withFrame:CGRectMake(
2618 0, screenrect.size.height - 48,
2619 screenrect.size.width, 48
2620 )
2621 withItemList:buttonitems
2622 ];
2623
2624 [buttonbar_ setDelegate:self];
2625 [buttonbar_ setBarStyle:1];
2626 [buttonbar_ setButtonBarTrackingMode:2];
2627
2628 int buttons[5] = {1, 2, 3, 4, 5};
2629 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
2630 [buttonbar_ showButtonGroup:0 withDuration:0];
2631
2632 for (int i = 0; i != 5; ++i)
2633 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
2634 i * 64 + 2, 1, 60, 48
2635 )];
2636
2637 [buttonbar_ showSelectionForButton:1];
2638 [transition_ transition:0 toView:featured_];
2639
2640 [overlay_ addSubview:buttonbar_];
2641
2642 database_ = [[Database alloc] init];
2643 [database_ setDelegate:progress_];
2644
2645 install_ = [[InstallView alloc] initWithFrame:[transition_ bounds] database:database_];
2646 [install_ setDelegate:self];
2647
2648 upgrade_ = [[UpgradeView alloc] initWithFrame:[transition_ bounds] database:database_];
2649 [upgrade_ setDelegate:self];
2650
2651 uninstall_ = [[UninstallView alloc] initWithFrame:[transition_ bounds] database:database_];
2652 [uninstall_ setDelegate:self];
2653
2654 sources_ = [[SourcesView alloc] initWithFrame:[transition_ bounds] database:database_];
2655 [sources_ setDelegate:self];
2656
2657 [self reloadData:NO];
2658 [progress_ resetView];
2659
2660 Package *package([database_ packageWithName:@"cydia"]);
2661 NSString *application = package == nil ? @"Cydia" : [NSString stringWithFormat:@"Cydia/%@", [package installed]];
2662 WebView *webview = [webview_ webView];
2663 [webview setApplicationNameForUserAgent:application];
2664
2665 url_ = [NSURL URLWithString:@"http://cydia.saurik.com/"];
2666 [self loadNews];
2667 }
2668
2669 @end
2670
2671 int main(int argc, char *argv[]) {
2672 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2673
2674 size_t size;
2675 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
2676 char *machine = new char[size];
2677 sysctlbyname("hw.machine", machine, &size, NULL, 0);
2678 Machine_ = machine;
2679
2680 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
2681 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
2682 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
2683 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
2684 CFRelease(serial);
2685 }
2686
2687 IOObjectRelease(service);
2688 }
2689
2690 UIApplicationMain(argc, argv, [Cydia class]);
2691 [pool release];
2692 }