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