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