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