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