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