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