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