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