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