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