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