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