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