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