]> git.saurik.com Git - cydia.git/blob - Cydia.mm
367c9b8204cfa947afa14893d881766bbe53ebcd
[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 bool reloading_;
2598 }
2599
2600 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2601 - (void) loadURL:(NSURL *)url;
2602
2603 - (void) loadRequest:(NSURLRequest *)request;
2604 - (void) reloadURL;
2605
2606 - (WebView *) webView;
2607
2608 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2609
2610 @end
2611 /* }}} */
2612
2613 /* Package View {{{ */
2614 @protocol PackageViewDelegate
2615 - (void) performPackage:(Package *)package;
2616 @end
2617
2618 @interface PackageView : RVPage {
2619 _transient Database *database_;
2620 UIPreferencesTable *table_;
2621 Package *package_;
2622 NSString *name_;
2623 UITextView *description_;
2624 }
2625
2626 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2627 - (void) setPackage:(Package *)package;
2628
2629 @end
2630
2631 @implementation PackageView
2632
2633 - (void) dealloc {
2634 [table_ setDataSource:nil];
2635 [table_ setDelegate:nil];
2636
2637 if (package_ != nil)
2638 [package_ release];
2639 if (name_ != nil)
2640 [name_ release];
2641 if (description_ != nil)
2642 [description_ release];
2643 [table_ release];
2644 [super dealloc];
2645 }
2646
2647 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2648 return [package_ source] == nil ? 2 : 3;
2649 }
2650
2651 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2652 switch (group) {
2653 case 0: return nil;
2654 case 1: return @"Package Details";
2655 case 2: return @"Source Information";
2656
2657 default: _assert(false);
2658 }
2659 }
2660
2661 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2662 if (group != 0 || row != 1)
2663 return proposed;
2664 else
2665 return [description_ visibleTextRect].size.height + TextViewOffset_;
2666 }
2667
2668 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2669 switch (group) {
2670 case 0: return [package_ website] == nil ? 2 : 3;
2671 case 1: return 5;
2672 case 2: return 3;
2673
2674 default: _assert(false);
2675 }
2676 }
2677
2678 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2679 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2680 [cell setShowSelection:NO];
2681
2682 switch (group) {
2683 case 0: switch (row) {
2684 case 0:
2685 [cell setTitle:[package_ name]];
2686 [cell setValue:[package_ latest]];
2687 break;
2688
2689 case 1:
2690 [cell addSubview:description_];
2691 break;
2692
2693 case 2:
2694 [cell setTitle:@"More Information"];
2695 [cell setShowDisclosure:YES];
2696 [cell setShowSelection:YES];
2697 break;
2698
2699 default: _assert(false);
2700 } break;
2701
2702 case 1: switch (row) {
2703 case 0:
2704 [cell setTitle:@"Identifier"];
2705 [cell setValue:[package_ id]];
2706 break;
2707
2708 case 1: {
2709 [cell setTitle:@"Installed Version"];
2710 NSString *installed([package_ installed]);
2711 [cell setValue:(installed == nil ? @"n/a" : installed)];
2712 } break;
2713
2714 case 2: {
2715 [cell setTitle:@"Section"];
2716 NSString *section([package_ section]);
2717 [cell setValue:(section == nil ? @"n/a" : section)];
2718 } break;
2719
2720 case 3:
2721 [cell setTitle:@"Expanded Size"];
2722 [cell setValue:SizeString([package_ size])];
2723 break;
2724
2725 case 4:
2726 [cell setTitle:@"Maintainer"];
2727 [cell setValue:[[package_ maintainer] name]];
2728 [cell setShowDisclosure:YES];
2729 [cell setShowSelection:YES];
2730 break;
2731
2732 default: _assert(false);
2733 } break;
2734
2735 case 2: switch (row) {
2736 case 0:
2737 [cell setTitle:[[package_ source] label]];
2738 [cell setValue:[[package_ source] version]];
2739 break;
2740
2741 case 1:
2742 [cell setValue:[[package_ source] description]];
2743 break;
2744
2745 case 2:
2746 [cell setTitle:@"Origin"];
2747 [cell setValue:[[package_ source] origin]];
2748 break;
2749
2750 default: _assert(false);
2751 } break;
2752
2753 default: _assert(false);
2754 }
2755
2756 return cell;
2757 }
2758
2759 - (BOOL) canSelectRow:(int)row {
2760 return YES;
2761 }
2762
2763 - (void) tableRowSelected:(NSNotification *)notification {
2764 int row = [table_ selectedRow];
2765 NSString *website = [package_ website];
2766
2767 if (row == (website == nil ? 8 : 9))
2768 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2769 [[package_ maintainer] email],
2770 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2771 ]]];
2772 else if (website != nil && row == 3) {
2773 NSURL *url = [NSURL URLWithString:website];
2774 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2775 [browser setDelegate:delegate_];
2776 [book_ pushPage:browser];
2777 [browser loadURL:url];
2778 }
2779 }
2780
2781 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2782 switch (button) {
2783 case 1: [delegate_ installPackage:package_]; break;
2784 case 2: [delegate_ removePackage:package_]; break;
2785 }
2786
2787 [sheet dismiss];
2788 }
2789
2790 - (void) _rightButtonClicked {
2791 if ([package_ installed] == nil)
2792 [delegate_ installPackage:package_];
2793 else {
2794 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2795
2796 if ([package_ upgradable])
2797 [buttons addObject:@"Upgrade"];
2798 else
2799 [buttons addObject:@"Reinstall"];
2800
2801 [buttons addObject:@"Remove"];
2802 [buttons addObject:@"Cancel"];
2803
2804 [delegate_ slideUp:[[[UIAlertSheet alloc]
2805 initWithTitle:@"Manage Package"
2806 buttons:buttons
2807 defaultButtonIndex:2
2808 delegate:self
2809 context:self
2810 ] autorelease]];
2811 }
2812 }
2813
2814 - (NSString *) rightButtonTitle {
2815 _assert(package_ != nil);
2816 return [package_ installed] == nil ? @"Install" : @"Modify";
2817 }
2818
2819 - (NSString *) title {
2820 return @"Details";
2821 }
2822
2823 - (id) initWithBook:(RVBook *)book database:(Database *)database {
2824 if ((self = [super initWithBook:book]) != nil) {
2825 database_ = database;
2826
2827 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2828 [self addSubview:table_];
2829
2830 [table_ setDataSource:self];
2831 [table_ setDelegate:self];
2832 } return self;
2833 }
2834
2835 - (void) setPackage:(Package *)package {
2836 if (package_ != nil) {
2837 [package_ autorelease];
2838 package_ = nil;
2839 }
2840
2841 if (name_ != nil) {
2842 [name_ release];
2843 name_ = nil;
2844 }
2845
2846 if (description_ != nil) {
2847 [description_ release];
2848 description_ = nil;
2849 }
2850
2851 if (package != nil) {
2852 package_ = [package retain];
2853 name_ = [[package id] retain];
2854
2855 NSString *description([package description]);
2856 if (description == nil)
2857 description = [package tagline];
2858 description_ = [GetTextView(description, 12, true) retain];
2859
2860 [description_ setTextColor:Black_];
2861
2862 [table_ reloadData];
2863 }
2864 }
2865
2866 - (void) resetViewAnimated:(BOOL)animated {
2867 [table_ resetViewAnimated:animated];
2868 }
2869
2870 - (void) reloadData {
2871 [self setPackage:[database_ packageWithName:name_]];
2872 [self reloadButtons];
2873 }
2874
2875 @end
2876 /* }}} */
2877 /* Package Table {{{ */
2878 @interface PackageTable : RVPage {
2879 _transient Database *database_;
2880 NSString *title_;
2881 SEL filter_;
2882 id object_;
2883 NSMutableArray *packages_;
2884 NSMutableArray *sections_;
2885 UISectionList *list_;
2886 }
2887
2888 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
2889
2890 - (void) setDelegate:(id)delegate;
2891 - (void) setObject:(id)object;
2892
2893 - (void) reloadData;
2894 - (void) resetCursor;
2895
2896 @end
2897
2898 @implementation PackageTable
2899
2900 - (void) dealloc {
2901 [list_ setDataSource:nil];
2902
2903 [title_ release];
2904 if (object_ != nil)
2905 [object_ release];
2906 [packages_ release];
2907 [sections_ release];
2908 [list_ release];
2909 [super dealloc];
2910 }
2911
2912 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
2913 return [sections_ count];
2914 }
2915
2916 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2917 return [[sections_ objectAtIndex:section] name];
2918 }
2919
2920 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2921 return [[sections_ objectAtIndex:section] row];
2922 }
2923
2924 - (int) numberOfRowsInTable:(UITable *)table {
2925 return [packages_ count];
2926 }
2927
2928 - (float) table:(UITable *)table heightForRow:(int)row {
2929 return 73;
2930 }
2931
2932 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2933 if (reusing == nil)
2934 reusing = [[[PackageCell alloc] init] autorelease];
2935 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2936 return reusing;
2937 }
2938
2939 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2940 return NO;
2941 }
2942
2943 - (void) tableRowSelected:(NSNotification *)notification {
2944 int row = [[notification object] selectedRow];
2945 if (row == INT_MAX)
2946 return;
2947
2948 Package *package = [packages_ objectAtIndex:row];
2949 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2950 [view setDelegate:delegate_];
2951 [view setPackage:package];
2952 [book_ pushPage:view];
2953 }
2954
2955 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2956 if ((self = [super initWithBook:book]) != nil) {
2957 database_ = database;
2958 title_ = [title retain];
2959 filter_ = filter;
2960 object_ = object == nil ? nil : [object retain];
2961
2962 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
2963 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2964
2965 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2966 [list_ setDataSource:self];
2967
2968 UITableColumn *column = [[[UITableColumn alloc]
2969 initWithTitle:@"Name"
2970 identifier:@"name"
2971 width:[self frame].size.width
2972 ] autorelease];
2973
2974 UITable *table = [list_ table];
2975 [table setSeparatorStyle:1];
2976 [table addTableColumn:column];
2977 [table setDelegate:self];
2978 [table setReusesTableCells:YES];
2979
2980 [self addSubview:list_];
2981 [self reloadData];
2982 } return self;
2983 }
2984
2985 - (void) setDelegate:(id)delegate {
2986 delegate_ = delegate;
2987 }
2988
2989 - (void) setObject:(id)object {
2990 if (object_ != nil)
2991 [object_ release];
2992 if (object == nil)
2993 object_ = nil;
2994 else
2995 object_ = [object retain];
2996 }
2997
2998 - (void) reloadData {
2999 NSArray *packages = [database_ packages];
3000
3001 [packages_ removeAllObjects];
3002 [sections_ removeAllObjects];
3003
3004 for (size_t i(0); i != [packages count]; ++i) {
3005 Package *package([packages objectAtIndex:i]);
3006 if ([[package performSelector:filter_ withObject:object_] boolValue])
3007 [packages_ addObject:package];
3008 }
3009
3010 [packages_ sortUsingSelector:@selector(compareByName:)];
3011
3012 Section *section = nil;
3013
3014 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3015 Package *package = [packages_ objectAtIndex:offset];
3016 NSString *name = [package index];
3017
3018 if (section == nil || ![[section name] isEqualToString:name]) {
3019 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3020 [sections_ addObject:section];
3021 }
3022
3023 [section addToCount];
3024 }
3025
3026 [list_ reloadData];
3027 }
3028
3029 - (NSString *) title {
3030 return title_;
3031 }
3032
3033 - (void) resetViewAnimated:(BOOL)animated {
3034 [list_ resetViewAnimated:animated];
3035 }
3036
3037 - (void) resetCursor {
3038 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3039 }
3040
3041 @end
3042 /* }}} */
3043
3044 /* Browser Implementation {{{ */
3045 @implementation BrowserView
3046
3047 - (void) dealloc {
3048 WebView *webview = [webview_ webView];
3049 [webview setFrameLoadDelegate:nil];
3050 [webview setResourceLoadDelegate:nil];
3051 [webview setUIDelegate:nil];
3052
3053 [scroller_ setDelegate:nil];
3054 [webview_ setDelegate:nil];
3055
3056 [scroller_ release];
3057 [webview_ release];
3058 [urls_ release];
3059 [indicator_ release];
3060 if (title_ != nil)
3061 [title_ release];
3062 [super dealloc];
3063 }
3064
3065 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3066 NSMutableURLRequest *request = [NSMutableURLRequest
3067 requestWithURL:url
3068 cachePolicy:policy
3069 timeoutInterval:30.0
3070 ];
3071
3072 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3073 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3074 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3075
3076 [self loadRequest:request];
3077 }
3078
3079
3080 - (void) loadURL:(NSURL *)url {
3081 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3082 }
3083
3084 // XXX: this needs to add the headers
3085 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3086 return request;
3087 }
3088
3089 - (void) loadRequest:(NSURLRequest *)request {
3090 [webview_ loadRequest:request];
3091 }
3092
3093 - (void) reloadURL {
3094 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3095 [urls_ removeLastObject];
3096 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3097 }
3098
3099 - (WebView *) webView {
3100 return [webview_ webView];
3101 }
3102
3103 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3104 [scroller_ setContentSize:frame.size];
3105 }
3106
3107 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3108 [self view:sender didSetFrame:frame];
3109 }
3110
3111 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3112 return [self _addHeadersToRequest:request];
3113 }
3114
3115 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3116 if ([[[request URL] scheme] isEqualToString:@"apptapp"])
3117 return nil;
3118 [self setBackButtonTitle:title_];
3119 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3120 [browser setDelegate:delegate_];
3121 [book_ pushPage:browser];
3122 [browser loadRequest:[self _addHeadersToRequest:request]];
3123 return [browser webView];
3124 }
3125
3126 - (void) webView:(WebView *)sender willClickElement:(id)element {
3127 if (![element respondsToSelector:@selector(href)])
3128 return;
3129 NSString *href = [element href];
3130 if (href == nil)
3131 return;
3132 if ([href hasPrefix:@"apptapp://package/"]) {
3133 NSString *name = [href substringFromIndex:18];
3134 Package *package = [database_ packageWithName:name];
3135 if (package == nil) {
3136 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3137 initWithTitle:@"Cannot Locate Package"
3138 buttons:[NSArray arrayWithObjects:@"Close", nil]
3139 defaultButtonIndex:0
3140 delegate:self
3141 context:self
3142 ] autorelease];
3143
3144 [sheet setBodyText:[NSString stringWithFormat:
3145 @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
3146 , name]];
3147
3148 [sheet popupAlertAnimated:YES];
3149 } else {
3150 [self setBackButtonTitle:title_];
3151 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3152 [view setDelegate:delegate_];
3153 [view setPackage:package];
3154 [book_ pushPage:view];
3155 }
3156 }
3157 }
3158
3159 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3160 title_ = [title retain];
3161 [self setTitle:title];
3162 }
3163
3164 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3165 if ([frame parentFrame] != nil)
3166 return;
3167
3168 reloading_ = false;
3169 loading_ = true;
3170 [indicator_ startAnimation];
3171 [self reloadButtons];
3172
3173 if (title_ != nil) {
3174 [title_ release];
3175 title_ = nil;
3176 }
3177
3178 [self setTitle:@"Loading..."];
3179
3180 WebView *webview = [webview_ webView];
3181 NSString *href = [webview mainFrameURL];
3182 [urls_ addObject:[NSURL URLWithString:href]];
3183
3184 CGRect webrect = [scroller_ frame];
3185 webrect.size.height = 0;
3186 [webview_ setFrame:webrect];
3187 }
3188
3189 - (void) _finishLoading {
3190 if (!reloading_) {
3191 loading_ = false;
3192 [indicator_ stopAnimation];
3193 [self reloadButtons];
3194 }
3195 }
3196
3197 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3198 if ([frame parentFrame] != nil)
3199 return;
3200 [self _finishLoading];
3201 }
3202
3203 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3204 if ([frame parentFrame] != nil)
3205 return;
3206 [self setTitle:[error localizedDescription]];
3207 [self _finishLoading];
3208 }
3209
3210 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3211 if ((self = [super initWithBook:book]) != nil) {
3212 database_ = database;
3213 loading_ = false;
3214
3215 struct CGRect bounds = [self bounds];
3216
3217 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3218 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3219 [self addSubview:pinstripe];
3220
3221 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3222 [self addSubview:scroller_];
3223
3224 [scroller_ setScrollingEnabled:YES];
3225 [scroller_ setAdjustForContentSizeChange:YES];
3226 [scroller_ setClipsSubviews:YES];
3227 [scroller_ setAllowsRubberBanding:YES];
3228 [scroller_ setScrollDecelerationFactor:0.99];
3229 [scroller_ setDelegate:self];
3230
3231 CGRect webrect = [scroller_ bounds];
3232 webrect.size.height = 0;
3233
3234 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3235 [scroller_ addSubview:webview_];
3236
3237 [webview_ setTilingEnabled:YES];
3238 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3239 [webview_ setAutoresizes:YES];
3240 [webview_ setDelegate:self];
3241 //[webview_ setEnabledGestures:2];
3242
3243 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3244 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3245 [indicator_ setStyle:0];
3246
3247 Package *package([database_ packageWithName:@"cydia"]);
3248 NSString *application = package == nil ? @"Cydia" : [NSString
3249 stringWithFormat:@"Cydia/%@",
3250 [package installed]
3251 ];
3252
3253 WebView *webview = [webview_ webView];
3254 [webview setApplicationNameForUserAgent:application];
3255 [webview setFrameLoadDelegate:self];
3256 [webview setResourceLoadDelegate:self];
3257 [webview setUIDelegate:self];
3258
3259 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3260 } return self;
3261 }
3262
3263 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3264 [sheet dismiss];
3265 }
3266
3267 - (void) _leftButtonClicked {
3268 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3269 initWithTitle:@"About Cydia Packager"
3270 buttons:[NSArray arrayWithObjects:@"Close", nil]
3271 defaultButtonIndex:0
3272 delegate:self
3273 context:self
3274 ] autorelease];
3275
3276 [sheet setBodyText:
3277 @"Copyright (C) 2008\n"
3278 "Jay Freeman (saurik)\n"
3279 "saurik@saurik.com\n"
3280 "http://www.saurik.com/\n"
3281 "\n"
3282 "The Okori Group\n"
3283 "http://www.theokorigroup.com/\n"
3284 "\n"
3285 "College of Creative Studies,\n"
3286 "University of California,\n"
3287 "Santa Barbara\n"
3288 "http://www.ccs.ucsb.edu/"
3289 ];
3290
3291 [sheet popupAlertAnimated:YES];
3292 }
3293
3294 - (void) _rightButtonClicked {
3295 reloading_ = true;
3296 [self reloadURL];
3297 }
3298
3299 - (NSString *) leftButtonTitle {
3300 return @"About";
3301 }
3302
3303 - (NSString *) rightButtonTitle {
3304 return loading_ ? @"" : @"Reload";
3305 }
3306
3307 - (NSString *) title {
3308 return nil;
3309 }
3310
3311 - (NSString *) backButtonTitle {
3312 return @"Browser";
3313 }
3314
3315 - (void) setPageActive:(BOOL)active {
3316 if (active)
3317 [book_ addSubview:indicator_];
3318 else
3319 [indicator_ removeFromSuperview];
3320 }
3321
3322 - (void) resetViewAnimated:(BOOL)animated {
3323 }
3324
3325 @end
3326 /* }}} */
3327
3328 @interface CYBook : RVBook <
3329 ProgressDelegate
3330 > {
3331 _transient Database *database_;
3332 UIView *overlay_;
3333 UIProgressIndicator *indicator_;
3334 UITextLabel *prompt_;
3335 UIProgressBar *progress_;
3336 bool updating_;
3337 }
3338
3339 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3340 - (void) update;
3341 - (BOOL) updating;
3342
3343 @end
3344
3345 /* Install View {{{ */
3346 @interface InstallView : RVPage {
3347 _transient Database *database_;
3348 NSMutableArray *packages_;
3349 NSMutableArray *sections_;
3350 UITable *list_;
3351 }
3352
3353 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3354 - (void) reloadData;
3355
3356 @end
3357
3358 @implementation InstallView
3359
3360 - (void) dealloc {
3361 [list_ setDataSource:nil];
3362 [list_ setDelegate:nil];
3363
3364 [packages_ release];
3365 [sections_ release];
3366 [list_ release];
3367 [super dealloc];
3368 }
3369
3370 - (int) numberOfRowsInTable:(UITable *)table {
3371 return [sections_ count] + 1;
3372 }
3373
3374 - (float) table:(UITable *)table heightForRow:(int)row {
3375 return 45;
3376 }
3377
3378 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3379 if (reusing == nil)
3380 reusing = [[[SectionCell alloc] init] autorelease];
3381 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
3382 return reusing;
3383 }
3384
3385 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3386 return YES;
3387 }
3388
3389 - (void) tableRowSelected:(NSNotification *)notification {
3390 int row = [[notification object] selectedRow];
3391 if (row == INT_MAX)
3392 return;
3393
3394 Section *section;
3395 NSString *title;
3396
3397 if (row == 0) {
3398 section = nil;
3399 title = @"All Packages";
3400 } else {
3401 section = [sections_ objectAtIndex:(row - 1)];
3402 title = [section name];
3403 }
3404
3405 PackageTable *table = [[[PackageTable alloc]
3406 initWithBook:book_
3407 database:database_
3408 title:title
3409 filter:@selector(isUninstalledInSection:)
3410 with:(section == nil ? nil : [section name])
3411 ] autorelease];
3412
3413 [table setDelegate:delegate_];
3414
3415 [book_ pushPage:table];
3416 }
3417
3418 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3419 if ((self = [super initWithBook:book]) != nil) {
3420 database_ = database;
3421
3422 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3423 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3424
3425 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3426 [self addSubview:list_];
3427
3428 UITableColumn *column = [[[UITableColumn alloc]
3429 initWithTitle:@"Name"
3430 identifier:@"name"
3431 width:[self frame].size.width
3432 ] autorelease];
3433
3434 [list_ setDataSource:self];
3435 [list_ setSeparatorStyle:1];
3436 [list_ addTableColumn:column];
3437 [list_ setDelegate:self];
3438 [list_ setReusesTableCells:YES];
3439
3440 [self reloadData];
3441 } return self;
3442 }
3443
3444 - (void) reloadData {
3445 NSArray *packages = [database_ packages];
3446
3447 [packages_ removeAllObjects];
3448 [sections_ removeAllObjects];
3449
3450 for (size_t i(0); i != [packages count]; ++i) {
3451 Package *package([packages objectAtIndex:i]);
3452 if ([package installed] == nil)
3453 [packages_ addObject:package];
3454 }
3455
3456 [packages_ sortUsingSelector:@selector(compareBySection:)];
3457
3458 Section *section = nil;
3459 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3460 Package *package = [packages_ objectAtIndex:offset];
3461 NSString *name = [package section];
3462
3463 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
3464 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3465 [sections_ addObject:section];
3466 }
3467
3468 [section addToCount];
3469 }
3470
3471 [list_ reloadData];
3472 }
3473
3474 - (void) resetViewAnimated:(BOOL)animated {
3475 [list_ resetViewAnimated:animated];
3476 }
3477
3478 - (NSString *) title {
3479 return @"Install";
3480 }
3481
3482 - (NSString *) backButtonTitle {
3483 return @"Sections";
3484 }
3485
3486 @end
3487 /* }}} */
3488 /* Changes View {{{ */
3489 @interface ChangesView : RVPage {
3490 _transient Database *database_;
3491 NSMutableArray *packages_;
3492 NSMutableArray *sections_;
3493 UISectionList *list_;
3494 unsigned upgrades_;
3495 }
3496
3497 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3498 - (void) reloadData;
3499
3500 @end
3501
3502 @implementation ChangesView
3503
3504 - (void) dealloc {
3505 [[list_ table] setDelegate:nil];
3506 [list_ setDataSource:nil];
3507
3508 [packages_ release];
3509 [sections_ release];
3510 [list_ release];
3511 [super dealloc];
3512 }
3513
3514 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3515 return [sections_ count];
3516 }
3517
3518 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3519 return [[sections_ objectAtIndex:section] name];
3520 }
3521
3522 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3523 return [[sections_ objectAtIndex:section] row];
3524 }
3525
3526 - (int) numberOfRowsInTable:(UITable *)table {
3527 return [packages_ count];
3528 }
3529
3530 - (float) table:(UITable *)table heightForRow:(int)row {
3531 return 73;
3532 }
3533
3534 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3535 if (reusing == nil)
3536 reusing = [[[PackageCell alloc] init] autorelease];
3537 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3538 return reusing;
3539 }
3540
3541 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3542 return NO;
3543 }
3544
3545 - (void) tableRowSelected:(NSNotification *)notification {
3546 int row = [[notification object] selectedRow];
3547 if (row == INT_MAX)
3548 return;
3549 Package *package = [packages_ objectAtIndex:row];
3550 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3551 [view setDelegate:delegate_];
3552 [view setPackage:package];
3553 [book_ pushPage:view];
3554 }
3555
3556 - (void) _leftButtonClicked {
3557 [(CYBook *)book_ update];
3558 [self reloadButtons];
3559 }
3560
3561 - (void) _rightButtonClicked {
3562 [delegate_ distUpgrade];
3563 }
3564
3565 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3566 if ((self = [super initWithBook:book]) != nil) {
3567 database_ = database;
3568
3569 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3570 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3571
3572 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3573 [self addSubview:list_];
3574
3575 [list_ setShouldHideHeaderInShortLists:NO];
3576 [list_ setDataSource:self];
3577 //[list_ setSectionListStyle:1];
3578
3579 UITableColumn *column = [[[UITableColumn alloc]
3580 initWithTitle:@"Name"
3581 identifier:@"name"
3582 width:[self frame].size.width
3583 ] autorelease];
3584
3585 UITable *table = [list_ table];
3586 [table setSeparatorStyle:1];
3587 [table addTableColumn:column];
3588 [table setDelegate:self];
3589 [table setReusesTableCells:YES];
3590
3591 [self reloadData];
3592 } return self;
3593 }
3594
3595 - (void) reloadData {
3596 NSArray *packages = [database_ packages];
3597
3598 [packages_ removeAllObjects];
3599 [sections_ removeAllObjects];
3600
3601 for (size_t i(0); i != [packages count]; ++i) {
3602 Package *package([packages objectAtIndex:i]);
3603 if ([package installed] == nil || [package upgradable])
3604 [packages_ addObject:package];
3605 }
3606
3607 [packages_ sortUsingSelector:@selector(compareForChanges:)];
3608
3609 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
3610 Section *section = nil;
3611
3612 upgrades_ = 0;
3613 bool unseens = false;
3614
3615 CFLocaleRef locale = CFLocaleCopyCurrent();
3616 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3617
3618 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3619 Package *package = [packages_ objectAtIndex:offset];
3620
3621 if ([package upgradable]) {
3622 ++upgrades_;
3623 [upgradable addToCount];
3624 } else {
3625 unseens = true;
3626 NSDate *seen = [package seen];
3627
3628 NSString *name;
3629
3630 if (seen == nil)
3631 name = [@"n/a ?" retain];
3632 else {
3633 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
3634 }
3635
3636 if (section == nil || ![[section name] isEqualToString:name]) {
3637 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3638 [sections_ addObject:section];
3639 }
3640
3641 [name release];
3642 [section addToCount];
3643 }
3644 }
3645
3646 CFRelease(formatter);
3647 CFRelease(locale);
3648
3649 if (unseens) {
3650 Section *last = [sections_ lastObject];
3651 size_t count = [last count];
3652 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3653 [sections_ removeLastObject];
3654 }
3655
3656 if (upgrades_ != 0)
3657 [sections_ insertObject:upgradable atIndex:0];
3658
3659 [list_ reloadData];
3660 [self reloadButtons];
3661 }
3662
3663 - (void) resetViewAnimated:(BOOL)animated {
3664 [list_ resetViewAnimated:animated];
3665 }
3666
3667 - (NSString *) leftButtonTitle {
3668 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3669 }
3670
3671 - (NSString *) rightButtonTitle {
3672 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3673 }
3674
3675 - (NSString *) title {
3676 return @"Changes";
3677 }
3678
3679 @end
3680 /* }}} */
3681 /* Manage View {{{ */
3682 @interface ManageView : PackageTable {
3683 }
3684
3685 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3686
3687 @end
3688
3689 @implementation ManageView
3690
3691 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3692 if ((self = [super
3693 initWithBook:book
3694 database:database
3695 title:nil
3696 filter:@selector(isInstalledInSection:)
3697 with:nil
3698 ]) != nil) {
3699 } return self;
3700 }
3701
3702 - (NSString *) title {
3703 return @"Installed Packages";
3704 }
3705
3706 - (NSString *) backButtonTitle {
3707 return @"All Packages";
3708 }
3709
3710 @end
3711 /* }}} */
3712 /* Search View {{{ */
3713 @protocol SearchViewDelegate
3714 - (void) showKeyboard:(BOOL)show;
3715 @end
3716
3717 @interface SearchView : RVPage {
3718 UIView *accessory_;
3719 UISearchField *field_;
3720 UITransitionView *transition_;
3721 PackageTable *table_;
3722 UIPreferencesTable *advanced_;
3723 UIView *dimmed_;
3724 bool flipped_;
3725 }
3726
3727 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3728 - (void) reloadData;
3729
3730 @end
3731
3732 @implementation SearchView
3733
3734 - (void) dealloc {
3735 #ifndef __OBJC2__
3736 [[field_ textTraits] setEditingDelegate:nil];
3737 #endif
3738 [field_ setDelegate:nil];
3739
3740 [accessory_ release];
3741 [field_ release];
3742 [transition_ release];
3743 [table_ release];
3744 [advanced_ release];
3745 [dimmed_ release];
3746 [super dealloc];
3747 }
3748
3749 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3750 [delegate_ showKeyboard:YES];
3751 [table_ setEnabled:NO];
3752 [self addSubview:dimmed_];
3753 }
3754
3755 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
3756 [dimmed_ removeFromSuperview];
3757 [table_ setEnabled:YES];
3758 [delegate_ showKeyboard:NO];
3759 }
3760
3761 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
3762 NSString *text([field_ text]);
3763 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3764 }
3765
3766 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3767 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3768 return YES;
3769
3770 [self reloadData];
3771 [field_ resignFirstResponder];
3772 return NO;
3773 }
3774
3775 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3776 if ((self = [super initWithBook:book]) != nil) {
3777 CGRect pageBounds = [book_ pageBounds];
3778
3779 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
3780 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3781 [self addSubview:pinstripe];*/
3782
3783 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3784 CGColor dimmed(space_, 0, 0, 0, 0.5);
3785 [dimmed_ setBackgroundColor:dimmed];
3786
3787 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3788 [self addSubview:transition_];
3789
3790 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3791
3792 table_ = [[PackageTable alloc]
3793 initWithBook:book
3794 database:database
3795 title:nil
3796 filter:@selector(isSearchedForBy:)
3797 with:nil
3798 ];
3799
3800 [transition_ transition:0 toView:table_];
3801
3802 CGRect cnfrect = {{1, 38}, {17, 18}};
3803
3804 CGRect area;
3805 area.origin.x = cnfrect.size.width + 15;
3806 area.origin.y = 30;
3807 area.size.width = [self bounds].size.width - area.origin.x - 18;
3808 area.size.height = [UISearchField defaultHeight];
3809
3810 field_ = [[UISearchField alloc] initWithFrame:area];
3811
3812 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3813 [field_ setFont:font];
3814 CFRelease(font);
3815
3816 [field_ setPlaceholder:@"Package Names & Descriptions"];
3817 [field_ setPaddingTop:5];
3818 [field_ setDelegate:self];
3819
3820 #ifndef __OBJC2__
3821 UITextTraits *traits = [field_ textTraits];
3822 [traits setEditingDelegate:self];
3823 [traits setReturnKeyType:6];
3824 [traits setAutoCapsType:0];
3825 [traits setAutoCorrectionType:1];
3826 #endif
3827
3828 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3829 [configure setShowPressFeedback:YES];
3830 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
3831 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
3832
3833 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3834 [accessory_ addSubview:field_];
3835 [accessory_ addSubview:configure];
3836 } return self;
3837 }
3838
3839 - (void) flipPage {
3840 LKAnimation *animation = [LKTransition animation];
3841 [animation setType:@"oglFlip"];
3842 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3843 [animation setFillMode:@"extended"];
3844 [animation setTransitionFlags:3];
3845 [animation setDuration:10];
3846 [animation setSpeed:0.35];
3847 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
3848 [[transition_ _layer] addAnimation:animation forKey:0];
3849 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3850 flipped_ = !flipped_;
3851 }
3852
3853 - (void) configurePushed {
3854 [field_ resignFirstResponder];
3855 [self flipPage];
3856 }
3857
3858 - (void) resetViewAnimated:(BOOL)animated {
3859 if (flipped_)
3860 [self flipPage];
3861 [table_ resetViewAnimated:animated];
3862 }
3863
3864 - (void) reloadData {
3865 if (flipped_)
3866 [self flipPage];
3867 [table_ setObject:[field_ text]];
3868 [table_ reloadData];
3869 [table_ resetCursor];
3870 }
3871
3872 - (UIView *) accessoryView {
3873 return accessory_;
3874 }
3875
3876 - (NSString *) title {
3877 return nil;
3878 }
3879
3880 - (NSString *) backButtonTitle {
3881 return @"Search";
3882 }
3883
3884 - (void) setDelegate:(id)delegate {
3885 [table_ setDelegate:delegate];
3886 [super setDelegate:delegate];
3887 }
3888
3889 @end
3890 /* }}} */
3891
3892 @implementation CYBook
3893
3894 - (void) dealloc {
3895 [overlay_ release];
3896 [indicator_ release];
3897 [prompt_ release];
3898 [progress_ release];
3899 [super dealloc];
3900 }
3901
3902 - (BOOL) updating {
3903 return updating_;
3904 }
3905
3906 - (void) update {
3907 [navbar_ setPrompt:@""];
3908 [navbar_ addSubview:overlay_];
3909 [indicator_ startAnimation];
3910 [prompt_ setText:@"Updating Database..."];
3911 [progress_ setProgress:0];
3912
3913 updating_ = true;
3914
3915 [NSThread
3916 detachNewThreadSelector:@selector(_update)
3917 toTarget:self
3918 withObject:nil
3919 ];
3920 }
3921
3922 - (void) _update_ {
3923 updating_ = false;
3924
3925 [overlay_ removeFromSuperview];
3926 [indicator_ stopAnimation];
3927 [delegate_ reloadData];
3928
3929 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3930 }
3931
3932 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
3933 if ((self = [super initWithFrame:frame]) != nil) {
3934 database_ = database;
3935
3936 CGRect ovrrect = [navbar_ bounds];
3937 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3938
3939 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
3940
3941 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3942 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3943 CGRect indrect = {{indoffset, indoffset}, indsize};
3944
3945 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3946 [indicator_ setStyle:2];
3947 [overlay_ addSubview:indicator_];
3948
3949 CGSize prmsize = {200, indsize.width};
3950
3951 CGRect prmrect = {{
3952 indoffset * 2 + indsize.width,
3953 (ovrrect.size.height - prmsize.height) / 2
3954 }, prmsize};
3955
3956 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
3957
3958 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
3959
3960 [prompt_ setColor:White_];
3961 [prompt_ setBackgroundColor:Clear_];
3962 [prompt_ setFont:font];
3963
3964 CFRelease(font);
3965
3966 [overlay_ addSubview:prompt_];
3967
3968 CGSize prgsize = {75, 100};
3969
3970 CGRect prgrect = {{
3971 ovrrect.size.width - prgsize.width - 10,
3972 (ovrrect.size.height - prgsize.height) / 2
3973 } , prgsize};
3974
3975 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3976 [progress_ setStyle:0];
3977 [overlay_ addSubview:progress_];
3978 } return self;
3979 }
3980
3981 - (void) _update {
3982 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
3983
3984 Status status;
3985 status.setDelegate(self);
3986
3987 [database_ updateWithStatus:status];
3988
3989 [self
3990 performSelectorOnMainThread:@selector(_update_)
3991 withObject:nil
3992 waitUntilDone:NO
3993 ];
3994
3995 [pool release];
3996 }
3997
3998 - (void) setProgressError:(NSString *)error {
3999 [self
4000 performSelectorOnMainThread:@selector(_setProgressError:)
4001 withObject:error
4002 waitUntilDone:YES
4003 ];
4004 }
4005
4006 - (void) setProgressTitle:(NSString *)title {
4007 [self
4008 performSelectorOnMainThread:@selector(_setProgressTitle:)
4009 withObject:title
4010 waitUntilDone:YES
4011 ];
4012 }
4013
4014 - (void) setProgressPercent:(float)percent {
4015 }
4016
4017 - (void) addProgressOutput:(NSString *)output {
4018 [self
4019 performSelectorOnMainThread:@selector(_addProgressOutput:)
4020 withObject:output
4021 waitUntilDone:YES
4022 ];
4023 }
4024
4025 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4026 [sheet dismiss];
4027 }
4028
4029 - (void) _setProgressError:(NSString *)error {
4030 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4031 }
4032
4033 - (void) _setProgressTitle:(NSString *)title {
4034 [prompt_ setText:[title stringByAppendingString:@"..."]];
4035 }
4036
4037 - (void) _addProgressOutput:(NSString *)output {
4038 }
4039
4040 @end
4041
4042 @interface Cydia : UIApplication <
4043 ConfirmationViewDelegate,
4044 ProgressViewDelegate,
4045 SearchViewDelegate,
4046 CydiaDelegate
4047 > {
4048 UIWindow *window_;
4049
4050 UIView *underlay_;
4051 UIView *overlay_;
4052 CYBook *book_;
4053 UIButtonBar *buttonbar_;
4054
4055 ConfirmationView *confirm_;
4056
4057 Database *database_;
4058 ProgressView *progress_;
4059
4060 unsigned tag_;
4061
4062 UIKeyboard *keyboard_;
4063 }
4064
4065 @end
4066
4067 @implementation Cydia
4068
4069 - (void) _reloadData {
4070 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4071 [hud setText:@"Reloading Data"];
4072 [overlay_ addSubview:hud];
4073 [hud show:YES];*/
4074
4075 [database_ reloadData];
4076
4077 size_t count = 16;
4078
4079 if (Packages_ == nil) {
4080 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
4081 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4082 }
4083
4084 size_t changes(0);
4085
4086 NSArray *packages = [database_ packages];
4087 for (int i(0), e([packages count]); i != e; ++i) {
4088 Package *package = [packages objectAtIndex:i];
4089 if ([package upgradable])
4090 ++changes;
4091 }
4092
4093 if (changes != 0) {
4094 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
4095 [buttonbar_ setBadgeValue:badge forButton:3];
4096 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4097 [buttonbar_ setBadgeAnimated:YES forButton:3];
4098 [self setApplicationBadge:badge];
4099 } else {
4100 [buttonbar_ setBadgeValue:nil forButton:3];
4101 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4102 [buttonbar_ setBadgeAnimated:NO forButton:3];
4103 [self removeApplicationBadge];
4104 }
4105
4106 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4107
4108 [book_ reloadData];
4109 /*[hud show:NO];
4110 [hud removeFromSuperview];*/
4111 }
4112
4113 - (void) reloadData {
4114 @synchronized (self) {
4115 if (confirm_ == nil)
4116 [self _reloadData];
4117 }
4118 }
4119
4120 - (void) resolve {
4121 pkgProblemResolver *resolver = [database_ resolver];
4122
4123 resolver->InstallProtect();
4124 if (!resolver->Resolve(true))
4125 _error->Discard();
4126 }
4127
4128 - (void) perform {
4129 [database_ prepare];
4130
4131 if ([database_ cache]->BrokenCount() == 0)
4132 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4133 else {
4134 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4135 NSArray *packages = [database_ packages];
4136
4137 for (size_t i(0); i != [packages count]; ++i) {
4138 Package *package = [packages objectAtIndex:i];
4139 if ([package broken])
4140 [broken addObject:[package name]];
4141 }
4142
4143 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4144 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4145 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4146 defaultButtonIndex:0
4147 delegate:self
4148 context:self
4149 ] autorelease];
4150
4151 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4152 [sheet popupAlertAnimated:YES];
4153
4154 [self _reloadData];
4155 }
4156 }
4157
4158 - (void) installPackage:(Package *)package {
4159 @synchronized (self) {
4160 [package install];
4161 [self resolve];
4162 [self perform];
4163 }
4164 }
4165
4166 - (void) removePackage:(Package *)package {
4167 @synchronized (self) {
4168 [package remove];
4169 [self resolve];
4170 [self perform];
4171 }
4172 }
4173
4174 - (void) distUpgrade {
4175 @synchronized (self) {
4176 [database_ upgrade];
4177 [self perform];
4178 }
4179 }
4180
4181 - (void) cancel {
4182 @synchronized (self) {
4183 [confirm_ release];
4184 confirm_ = nil;
4185 [self _reloadData];
4186 }
4187 }
4188
4189 - (void) confirm {
4190 [overlay_ removeFromSuperview];
4191 restart_ = true;
4192
4193 [progress_
4194 detachNewThreadSelector:@selector(perform)
4195 toTarget:database_
4196 withObject:nil
4197 title:@"Running..."
4198 ];
4199 }
4200
4201 - (void) bootstrap_ {
4202 [database_ update];
4203 [database_ upgrade];
4204 [database_ prepare];
4205 [database_ perform];
4206 }
4207
4208 - (void) bootstrap {
4209 [progress_
4210 detachNewThreadSelector:@selector(bootstrap_)
4211 toTarget:self
4212 withObject:nil
4213 title:@"Bootstrap Install..."
4214 ];
4215 }
4216
4217 - (void) progressViewIsComplete:(ProgressView *)progress {
4218 @synchronized (self) {
4219 [self _reloadData];
4220
4221 if (confirm_ != nil) {
4222 [underlay_ addSubview:overlay_];
4223 [confirm_ removeFromSuperview];
4224 [confirm_ release];
4225 confirm_ = nil;
4226 }
4227 }
4228 }
4229
4230 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4231 [sheet dismiss];
4232 }
4233
4234 - (void) setPage:(RVPage *)page {
4235 [page setDelegate:self];
4236 [book_ setPage:page];
4237 }
4238
4239 - (RVPage *) _setNewsPage {
4240 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4241 [self setPage:browser];
4242 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4243 return browser;
4244 }
4245
4246 - (void) buttonBarItemTapped:(id)sender {
4247 unsigned tag = [sender tag];
4248 if (tag == tag_) {
4249 [book_ resetViewAnimated:YES];
4250 return;
4251 }
4252
4253 switch (tag) {
4254 case 1:
4255 [self _setNewsPage];
4256 break;
4257
4258 case 2:
4259 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4260 break;
4261
4262 case 3:
4263 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4264 break;
4265
4266 case 4:
4267 [self setPage:[[[ManageView alloc] initWithBook:book_ database:database_] autorelease]];
4268 break;
4269
4270 case 5:
4271 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4272 break;
4273
4274 default:
4275 _assert(false);
4276 }
4277
4278 tag_ = tag;
4279 }
4280
4281 - (void) applicationWillSuspend {
4282 [super applicationWillSuspend];
4283
4284 if (restart_)
4285 if (FW_LEAST(1,1,3))
4286 notify_post("com.apple.language.changed");
4287 else
4288 system("launchctl stop com.apple.SpringBoard");
4289 }
4290
4291 - (void) applicationDidFinishLaunching:(id)unused {
4292 _assert(pkgInitConfig(*_config));
4293 _assert(pkgInitSystem(*_config, _system));
4294
4295 confirm_ = nil;
4296 tag_ = 1;
4297
4298 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4299 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4300
4301 [window_ orderFront: self];
4302 [window_ makeKey: self];
4303 [window_ _setHidden: NO];
4304
4305 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
4306 [window_ setContentView:progress_];
4307
4308 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4309 [progress_ setContentView:underlay_];
4310
4311 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
4312
4313 if (!bootstrap_)
4314 [underlay_ addSubview:overlay_];
4315
4316 database_ = [[Database alloc] init];
4317 [database_ setDelegate:progress_];
4318
4319 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4320 0, 0, screenrect.size.width, screenrect.size.height - 48
4321 ) database:database_];
4322
4323 [book_ setDelegate:self];
4324
4325 [overlay_ addSubview:book_];
4326
4327 NSArray *buttonitems = [NSArray arrayWithObjects:
4328 [NSDictionary dictionaryWithObjectsAndKeys:
4329 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4330 @"news-up.png", kUIButtonBarButtonInfo,
4331 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
4332 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4333 self, kUIButtonBarButtonTarget,
4334 @"News", kUIButtonBarButtonTitle,
4335 @"0", kUIButtonBarButtonType,
4336 nil],
4337
4338 [NSDictionary dictionaryWithObjectsAndKeys:
4339 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4340 @"install-up.png", kUIButtonBarButtonInfo,
4341 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4342 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4343 self, kUIButtonBarButtonTarget,
4344 @"Install", kUIButtonBarButtonTitle,
4345 @"0", kUIButtonBarButtonType,
4346 nil],
4347
4348 [NSDictionary dictionaryWithObjectsAndKeys:
4349 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4350 @"changes-up.png", kUIButtonBarButtonInfo,
4351 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
4352 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4353 self, kUIButtonBarButtonTarget,
4354 @"Changes", kUIButtonBarButtonTitle,
4355 @"0", kUIButtonBarButtonType,
4356 nil],
4357
4358 [NSDictionary dictionaryWithObjectsAndKeys:
4359 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4360 @"manage-up.png", kUIButtonBarButtonInfo,
4361 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
4362 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4363 self, kUIButtonBarButtonTarget,
4364 @"Manage", kUIButtonBarButtonTitle,
4365 @"0", kUIButtonBarButtonType,
4366 nil],
4367
4368 [NSDictionary dictionaryWithObjectsAndKeys:
4369 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4370 @"search-up.png", kUIButtonBarButtonInfo,
4371 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
4372 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4373 self, kUIButtonBarButtonTarget,
4374 @"Search", kUIButtonBarButtonTitle,
4375 @"0", kUIButtonBarButtonType,
4376 nil],
4377 nil];
4378
4379 buttonbar_ = [[UIButtonBar alloc]
4380 initInView:overlay_
4381 withFrame:CGRectMake(
4382 0, screenrect.size.height - 48,
4383 screenrect.size.width, 48
4384 )
4385 withItemList:buttonitems
4386 ];
4387
4388 [buttonbar_ setDelegate:self];
4389 [buttonbar_ setBarStyle:1];
4390 [buttonbar_ setButtonBarTrackingMode:2];
4391
4392 int buttons[5] = {1, 2, 3, 4, 5};
4393 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4394 [buttonbar_ showButtonGroup:0 withDuration:0];
4395
4396 for (int i = 0; i != 5; ++i)
4397 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
4398 i * 64 + 2, 1, 60, 48
4399 )];
4400
4401 [buttonbar_ showSelectionForButton:1];
4402 [overlay_ addSubview:buttonbar_];
4403
4404 [UIKeyboard initImplementationNow];
4405 CGSize keysize = [UIKeyboard defaultSize];
4406 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4407 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
4408 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
4409
4410 [self reloadData];
4411 [book_ update];
4412
4413 [progress_ resetView];
4414
4415 if (bootstrap_)
4416 [self bootstrap];
4417 else
4418 [self _setNewsPage];
4419 }
4420
4421 - (void) showKeyboard:(BOOL)show {
4422 if (show)
4423 [overlay_ addSubview:keyboard_];
4424 else
4425 [keyboard_ removeFromSuperview];
4426 }
4427
4428 - (void) slideUp:(UIAlertSheet *)alert {
4429 [alert presentSheetFromButtonBar:buttonbar_];
4430 }
4431
4432 @end
4433
4434 void AddPreferences(NSString *plist) {
4435 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4436
4437 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4438 _assert(settings != NULL);
4439 NSMutableArray *items = [settings objectForKey:@"items"];
4440
4441 bool cydia(false);
4442
4443 for (size_t i(0); i != [items count]; ++i) {
4444 NSMutableDictionary *item([items objectAtIndex:i]);
4445 NSString *label = [item objectForKey:@"label"];
4446 if (label != nil && [label isEqualToString:@"Cydia"]) {
4447 cydia = true;
4448 break;
4449 }
4450 }
4451
4452 if (!cydia) {
4453 for (size_t i(0); i != [items count]; ++i) {
4454 NSDictionary *item([items objectAtIndex:i]);
4455 NSString *label = [item objectForKey:@"label"];
4456 if (label != nil && [label isEqualToString:@"General"]) {
4457 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4458 @"CydiaSettings", @"bundle",
4459 @"PSLinkCell", @"cell",
4460 [NSNumber numberWithBool:YES], @"hasIcon",
4461 [NSNumber numberWithBool:YES], @"isController",
4462 @"Cydia", @"label",
4463 nil] atIndex:(i + 1)];
4464
4465 break;
4466 }
4467 }
4468
4469 _assert([settings writeToFile:plist atomically:YES] == YES);
4470 }
4471
4472 [pool release];
4473 }
4474
4475 /*IMP alloc_;
4476 id Alloc_(id self, SEL selector) {
4477 id object = alloc_(self, selector);
4478 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4479 return object;
4480 }*/
4481
4482 /*IMP dealloc_;
4483 id Dealloc_(id self, SEL selector) {
4484 id object = dealloc_(self, selector);
4485 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4486 return object;
4487 }*/
4488
4489 int main(int argc, char *argv[]) {
4490 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4491
4492 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4493
4494 Home_ = NSHomeDirectory();
4495
4496 {
4497 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4498 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4499 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4500 Sounds_Keyboard_ = [keyboard boolValue];
4501 }
4502
4503 setuid(0);
4504 setgid(0);
4505
4506 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4507 alloc_ = alloc->method_imp;
4508 alloc->method_imp = (IMP) &Alloc_;*/
4509
4510 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4511 dealloc_ = dealloc->method_imp;
4512 dealloc->method_imp = (IMP) &Dealloc_;*/
4513
4514 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4515 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
4516 Firmware_ = strdup([prover UTF8String]);
4517 NSArray *versions = [prover componentsSeparatedByString:@"."];
4518 int count = [versions count];
4519 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
4520 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4521 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
4522 }
4523 }
4524
4525 size_t size;
4526 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4527 char *machine = new char[size];
4528 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4529 Machine_ = machine;
4530
4531 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4532 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4533 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4534 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4535 CFRelease(serial);
4536 }
4537
4538 IOObjectRelease(service);
4539 }
4540
4541 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4542 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
4543
4544 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4545 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4546 else
4547 Packages_ = [Metadata_ objectForKey:@"Packages"];
4548
4549 setenv("CYDIA", "", _not(int));
4550 if (access("/User", F_OK) != 0)
4551 system("/usr/libexec/cydia/firmware.sh");
4552 system("dpkg --configure -a");
4553
4554 space_ = CGColorSpaceCreateDeviceRGB();
4555
4556 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4557 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4558 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4559 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4560
4561 int value = UIApplicationMain(argc, argv, [Cydia class]);
4562
4563 CGColorSpaceRelease(space_);
4564
4565 [pool release];
4566 return value;
4567 }