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