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