]> git.saurik.com Git - cydia.git/blob - Cydia.mm
Removed idle timer.
[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 #ifdef __OBJC2__
39 #define UITextTraits UITextInputTraits
40 #define textTraits textInputTraits
41 #define setAutoCapsType setAutocapitalizationType
42 #define setAutoCorrectionType setAutocorrectionType
43 #endif
44
45 /* #include Directives {{{ */
46 #include <objc/objc.h>
47 #include <objc/runtime.h>
48
49 #include <CoreGraphics/CoreGraphics.h>
50 #include <GraphicsServices/GraphicsServices.h>
51 #include <Foundation/Foundation.h>
52 #include <UIKit/UIKit.h>
53 #include <WebCore/DOMHTML.h>
54
55 #import "BrowserView.h"
56 #import "ResetView.h"
57 #import "UICaboodle.h"
58
59 #include <WebKit/WebFrame.h>
60 #include <WebKit/WebView.h>
61
62 #include <sstream>
63 #include <string>
64
65 #include <ext/stdio_filebuf.h>
66
67 #include <apt-pkg/acquire.h>
68 #include <apt-pkg/acquire-item.h>
69 #include <apt-pkg/algorithms.h>
70 #include <apt-pkg/cachefile.h>
71 #include <apt-pkg/clean.h>
72 #include <apt-pkg/configuration.h>
73 #include <apt-pkg/debmetaindex.h>
74 #include <apt-pkg/error.h>
75 #include <apt-pkg/init.h>
76 #include <apt-pkg/pkgrecords.h>
77 #include <apt-pkg/sourcelist.h>
78 #include <apt-pkg/sptr.h>
79
80 #include <sys/sysctl.h>
81 #include <notify.h>
82
83 extern "C" {
84 #include <mach-o/nlist.h>
85 }
86
87 #include <cstdio>
88 #include <cstdlib>
89 #include <cstring>
90
91 #include <errno.h>
92 #include <pcre.h>
93 /* }}} */
94
95 /* iPhoneOS 2.0 Compatibility {{{ */
96 #ifdef __OBJC2__
97 @interface UICGColor : NSObject {
98 }
99
100 - (id) initWithCGColor:(CGColorRef)color;
101 @end
102
103 @interface UIFont {
104 }
105
106 - (UIFont *) fontWithSize:(CGFloat)size;
107 @end
108
109 @interface NSObject (iPhoneOS)
110 - (CGColorRef) cgColor;
111 - (CGColorRef) CGColor;
112 - (void) set;
113 @end
114
115 @implementation NSObject (iPhoneOS)
116
117 - (CGColorRef) cgColor {
118 return [self CGColor];
119 }
120
121 - (CGColorRef) CGColor {
122 return (CGColorRef) self;
123 }
124
125 - (void) set {
126 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
127 }
128
129 @end
130
131 @interface UITextView (iPhoneOS)
132 - (void) setTextSize:(float)size;
133 @end
134
135 @implementation UITextView (iPhoneOS)
136
137 - (void) setTextSize:(float)size {
138 [self setFont:[[self font] fontWithSize:size]];
139 }
140
141 @end
142 #endif
143 /* }}} */
144
145 @interface UIApplication (IdleTimer)
146 - (void) setIdleTimerDisabled:(char)arg0;
147 @end
148
149 #ifdef __OBJC2__
150 typedef enum {
151 kUIProgressIndicatorStyleMediumWhite = 1,
152 kUIProgressIndicatorStyleSmallWhite = 0,
153 kUIProgressIndicatorStyleSmallBlack = 4
154 } UIProgressIndicatorStyle;
155 #else
156 typedef enum {
157 kUIProgressIndicatorStyleMediumWhite = 0,
158 kUIProgressIndicatorStyleSmallWhite = 2,
159 kUIProgressIndicatorStyleSmallBlack = 3
160 } UIProgressIndicatorStyle;
161 #endif
162
163 typedef enum {
164 kUIControlEventMouseDown = 1 << 0,
165 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
166 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
167 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
168 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
169 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
170 } UIControlEventMasks;
171
172 @interface NSString (UIKit)
173 - (NSString *) stringByAddingPercentEscapes;
174 - (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
175 @end
176
177 @interface NSString (Cydia)
178 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
179 - (NSComparisonResult) compareByPath:(NSString *)other;
180 @end
181
182 @implementation NSString (Cydia)
183
184 + (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
185 char data[length + 1];
186 memcpy(data, bytes, length);
187 data[length] = '\0';
188 return [NSString stringWithUTF8String:data];
189 }
190
191 - (NSComparisonResult) compareByPath:(NSString *)other {
192 NSString *prefix = [self commonPrefixWithString:other options:0];
193 size_t length = [prefix length];
194
195 NSRange lrange = NSMakeRange(length, [self length] - length);
196 NSRange rrange = NSMakeRange(length, [other length] - length);
197
198 lrange = [self rangeOfString:@"/" options:0 range:lrange];
199 rrange = [other rangeOfString:@"/" options:0 range:rrange];
200
201 NSComparisonResult value;
202
203 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
204 value = NSOrderedSame;
205 else if (lrange.location == NSNotFound)
206 value = NSOrderedAscending;
207 else if (rrange.location == NSNotFound)
208 value = NSOrderedDescending;
209 else
210 value = NSOrderedSame;
211
212 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
213 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
214 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
215 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
216
217 NSComparisonResult result = [lpath compare:rpath];
218 return result == NSOrderedSame ? value : result;
219 }
220
221 @end
222
223 /* Perl-Compatible RegEx {{{ */
224 class Pcre {
225 private:
226 pcre *code_;
227 pcre_extra *study_;
228 int capture_;
229 int *matches_;
230 const char *data_;
231
232 public:
233 Pcre(const char *regex) :
234 study_(NULL)
235 {
236 const char *error;
237 int offset;
238 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
239
240 if (code_ == NULL) {
241 fprintf(stderr, "%d:%s\n", offset, error);
242 _assert(false);
243 }
244
245 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
246 matches_ = new int[(capture_ + 1) * 3];
247 }
248
249 ~Pcre() {
250 pcre_free(code_);
251 delete matches_;
252 }
253
254 NSString *operator [](size_t match) {
255 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
256 }
257
258 bool operator ()(NSString *data) {
259 // XXX: length is for characters, not for bytes
260 return operator ()([data UTF8String], [data length]);
261 }
262
263 bool operator ()(const char *data, size_t size) {
264 data_ = data;
265 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
266 }
267 };
268 /* }}} */
269 /* Mime Addresses {{{ */
270 Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
271
272 @interface Address : NSObject {
273 NSString *name_;
274 NSString *email_;
275 }
276
277 - (NSString *) name;
278 - (NSString *) email;
279
280 + (Address *) addressWithString:(NSString *)string;
281 - (Address *) initWithString:(NSString *)string;
282 @end
283
284 @implementation Address
285
286 - (void) dealloc {
287 [name_ release];
288 if (email_ != nil)
289 [email_ release];
290 [super dealloc];
291 }
292
293 - (NSString *) name {
294 return name_;
295 }
296
297 - (NSString *) email {
298 return email_;
299 }
300
301 + (Address *) addressWithString:(NSString *)string {
302 return [[[Address alloc] initWithString:string] autorelease];
303 }
304
305 - (Address *) initWithString:(NSString *)string {
306 if ((self = [super init]) != nil) {
307 const char *data = [string UTF8String];
308 size_t size = [string length];
309
310 if (email_r(data, size)) {
311 name_ = [email_r[1] retain];
312 email_ = [email_r[2] retain];
313 } else {
314 name_ = [[NSString alloc]
315 initWithBytes:data
316 length:size
317 encoding:kCFStringEncodingUTF8
318 ];
319
320 email_ = nil;
321 }
322 } return self;
323 }
324
325 @end
326 /* }}} */
327 /* CoreGraphics Primitives {{{ */
328 class CGColor {
329 private:
330 CGColorRef color_;
331
332 public:
333 CGColor() :
334 color_(NULL)
335 {
336 }
337
338 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
339 color_(NULL)
340 {
341 Set(space, red, green, blue, alpha);
342 }
343
344 void Clear() {
345 if (color_ != NULL)
346 CGColorRelease(color_);
347 }
348
349 ~CGColor() {
350 Clear();
351 }
352
353 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
354 Clear();
355 float color[] = {red, green, blue, alpha};
356 color_ = CGColorCreate(space, color);
357 }
358
359 operator CGColorRef() {
360 return color_;
361 }
362 };
363
364 class GSFont {
365 private:
366 GSFontRef font_;
367
368 public:
369 ~GSFont() {
370 CFRelease(font_);
371 }
372 };
373 /* }}} */
374 /* Right Alignment {{{ */
375 @interface UIRightTextLabel : UITextLabel {
376 float _savedRightEdgeX;
377 BOOL _sizedtofit_flag;
378 }
379
380 - (void) setFrame:(CGRect)frame;
381 - (void) setText:(NSString *)text;
382 - (void) realignText;
383 @end
384
385 @implementation UIRightTextLabel
386
387 - (void) setFrame:(CGRect)frame {
388 [super setFrame:frame];
389 if (_sizedtofit_flag == NO) {
390 _savedRightEdgeX = frame.origin.x;
391 [self realignText];
392 }
393 }
394
395 - (void) setText:(NSString *)text {
396 [super setText:text];
397 [self realignText];
398 }
399
400 - (void) realignText {
401 CGRect oldFrame = [self frame];
402
403 _sizedtofit_flag = YES;
404 [self sizeToFit]; // shrink down size so I can right align it
405
406 CGRect newFrame = [self frame];
407
408 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
409 oldFrame.size.width = newFrame.size.width;
410 [super setFrame:oldFrame];
411 _sizedtofit_flag = NO;
412 }
413
414 @end
415 /* }}} */
416
417 /* Random Global Variables {{{ */
418 static const int PulseInterval_ = 50000;
419 static const int ButtonBarHeight_ = 48;
420 static const float KeyboardTime_ = 0.3f;
421 static const char * const SpringBoard_ = "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist";
422
423 #ifndef Cydia_
424 #define Cydia_ ""
425 #endif
426
427 static CGColor Blueish_;
428 static CGColor Black_;
429 static CGColor Clear_;
430 static CGColor Red_;
431 static CGColor White_;
432
433 static NSString *Home_;
434 static BOOL Sounds_Keyboard_;
435
436 static BOOL Advanced_;
437 static BOOL Loaded_;
438 static BOOL Ignored_;
439
440 const char *Firmware_ = NULL;
441 const char *Machine_ = NULL;
442 const char *SerialNumber_ = NULL;
443
444 unsigned Major_;
445 unsigned Minor_;
446 unsigned BugFix_;
447
448 CFLocaleRef Locale_;
449 CGColorSpaceRef space_;
450
451 #define FW_LEAST(major, minor, bugfix) \
452 (major < Major_ || major == Major_ && \
453 (minor < Minor_ || minor == Minor_ && \
454 bugfix <= BugFix_))
455
456 bool bootstrap_;
457 bool reload_;
458
459 static NSMutableDictionary *Metadata_;
460 static NSMutableDictionary *Packages_;
461 static NSMutableDictionary *Sections_;
462 static bool Changed_;
463 static NSDate *now_;
464
465 NSString *GetLastUpdate() {
466 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
467
468 if (update == nil)
469 return @"Never or Unknown";
470
471 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
472 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
473
474 CFRelease(formatter);
475
476 return [(NSString *) formatted autorelease];
477 }
478 /* }}} */
479 /* Display Helpers {{{ */
480 inline float Interpolate(float begin, float end, float fraction) {
481 return (end - begin) * fraction + begin;
482 }
483
484 NSString *SizeString(double size) {
485 unsigned power = 0;
486 while (size > 1024) {
487 size /= 1024;
488 ++power;
489 }
490
491 static const char *powers_[] = {"B", "kB", "MB", "GB"};
492
493 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
494 }
495
496 NSString *StripVersion(NSString *version) {
497 NSRange colon = [version rangeOfString:@":"];
498 if (colon.location != NSNotFound)
499 version = [version substringFromIndex:(colon.location + 1)];
500 return version;
501 }
502
503 static const float TextViewOffset_ = 22;
504
505 UITextView *GetTextView(NSString *value, float left, bool html) {
506 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
507 [text setEditable:NO];
508 [text setTextSize:16];
509 /*if (html)
510 [text setHTML:value];
511 else*/
512 [text setText:value];
513 [text setEnabled:NO];
514
515 [text setBackgroundColor:Clear_];
516
517 CGRect frame = [text frame];
518 [text setFrame:frame];
519 CGRect rect = [text visibleTextRect];
520 frame.size.height = rect.size.height;
521 [text setFrame:frame];
522
523 return text;
524 }
525
526 NSString *Simplify(NSString *title) {
527 const char *data = [title UTF8String];
528 size_t size = [title length];
529
530 Pcre title_r("^(.*?)( \\(.*\\))?$");
531 if (title_r(data, size))
532 return title_r[1];
533 else
534 return title;
535 }
536 /* }}} */
537
538 bool isSectionVisible(NSString *section) {
539 NSDictionary *metadata = [Sections_ objectForKey:section];
540 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
541 return hidden == nil || ![hidden boolValue];
542 }
543
544 /* Delegate Prototypes {{{ */
545 @class Package;
546 @class Source;
547
548 @interface NSObject (ProgressDelegate)
549 @end
550
551 @implementation NSObject(ProgressDelegate)
552
553 - (void) _setProgressError:(NSArray *)args {
554 [self performSelector:@selector(setProgressError:forPackage:)
555 withObject:[args objectAtIndex:0]
556 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
557 ];
558 }
559
560 @end
561
562 @protocol ProgressDelegate
563 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
564 - (void) setProgressTitle:(NSString *)title;
565 - (void) setProgressPercent:(float)percent;
566 - (void) addProgressOutput:(NSString *)output;
567 @end
568
569 @protocol ConfigurationDelegate
570 - (void) repairWithSelector:(SEL)selector;
571 - (void) setConfigurationData:(NSString *)data;
572 @end
573
574 @protocol CydiaDelegate
575 - (void) installPackage:(Package *)package;
576 - (void) removePackage:(Package *)package;
577 - (void) slideUp:(UIAlertSheet *)alert;
578 - (void) distUpgrade;
579 - (void) updateData;
580 @end
581 /* }}} */
582
583 /* Status Delegation {{{ */
584 class Status :
585 public pkgAcquireStatus
586 {
587 private:
588 _transient NSObject<ProgressDelegate> *delegate_;
589
590 public:
591 Status() :
592 delegate_(nil)
593 {
594 }
595
596 void setDelegate(id delegate) {
597 delegate_ = delegate;
598 }
599
600 virtual bool MediaChange(std::string media, std::string drive) {
601 return false;
602 }
603
604 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
605 }
606
607 virtual void Fetch(pkgAcquire::ItemDesc &item) {
608 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
609 }
610
611 virtual void Done(pkgAcquire::ItemDesc &item) {
612 }
613
614 virtual void Fail(pkgAcquire::ItemDesc &item) {
615 if (
616 item.Owner->Status == pkgAcquire::Item::StatIdle ||
617 item.Owner->Status == pkgAcquire::Item::StatDone
618 )
619 return;
620
621 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
622 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:item.Owner->ErrorText.c_str()], nil]
623 waitUntilDone:YES
624 ];
625 }
626
627 virtual bool Pulse(pkgAcquire *Owner) {
628 bool value = pkgAcquireStatus::Pulse(Owner);
629
630 float percent(
631 double(CurrentBytes + CurrentItems) /
632 double(TotalBytes + TotalItems)
633 );
634
635 [delegate_ setProgressPercent:percent];
636 return value;
637 }
638
639 virtual void Start() {
640 }
641
642 virtual void Stop() {
643 }
644 };
645 /* }}} */
646 /* Progress Delegation {{{ */
647 class Progress :
648 public OpProgress
649 {
650 private:
651 _transient id<ProgressDelegate> delegate_;
652
653 protected:
654 virtual void Update() {
655 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
656 [delegate_ setProgressPercent:(Percent / 100)];
657 }
658
659 public:
660 Progress() :
661 delegate_(nil)
662 {
663 }
664
665 void setDelegate(id delegate) {
666 delegate_ = delegate;
667 }
668
669 virtual void Done() {
670 [delegate_ setProgressPercent:1];
671 }
672 };
673 /* }}} */
674
675 /* Database Interface {{{ */
676 @interface Database : NSObject {
677 pkgCacheFile cache_;
678 pkgDepCache::Policy *policy_;
679 pkgRecords *records_;
680 pkgProblemResolver *resolver_;
681 pkgAcquire *fetcher_;
682 FileFd *lock_;
683 SPtr<pkgPackageManager> manager_;
684 pkgSourceList *list_;
685
686 NSMutableDictionary *sources_;
687 NSMutableArray *packages_;
688
689 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
690 Status status_;
691 Progress progress_;
692
693 int cydiafd_;
694 int statusfd_;
695 FILE *input_;
696 }
697
698 - (void) _readCydia:(NSNumber *)fd;
699 - (void) _readStatus:(NSNumber *)fd;
700 - (void) _readOutput:(NSNumber *)fd;
701
702 - (FILE *) input;
703
704 - (Package *) packageWithName:(NSString *)name;
705
706 - (Database *) init;
707 - (pkgCacheFile &) cache;
708 - (pkgDepCache::Policy *) policy;
709 - (pkgRecords *) records;
710 - (pkgProblemResolver *) resolver;
711 - (pkgAcquire &) fetcher;
712 - (NSArray *) packages;
713 - (void) reloadData;
714
715 - (void) configure;
716 - (void) prepare;
717 - (void) perform;
718 - (void) upgrade;
719 - (void) update;
720
721 - (void) updateWithStatus:(Status &)status;
722
723 - (void) setDelegate:(id)delegate;
724 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
725 @end
726 /* }}} */
727
728 /* Source Class {{{ */
729 @interface Source : NSObject {
730 NSString *description_;
731 NSString *label_;
732 NSString *origin_;
733
734 NSString *uri_;
735 NSString *distribution_;
736 NSString *type_;
737 NSString *version_;
738
739 NSString *defaultIcon_;
740
741 BOOL trusted_;
742 }
743
744 - (Source *) initWithMetaIndex:(metaIndex *)index;
745
746 - (BOOL) trusted;
747
748 - (NSString *) uri;
749 - (NSString *) distribution;
750 - (NSString *) type;
751
752 - (NSString *) description;
753 - (NSString *) label;
754 - (NSString *) origin;
755 - (NSString *) version;
756
757 - (NSString *) defaultIcon;
758 @end
759
760 @implementation Source
761
762 - (void) dealloc {
763 [uri_ release];
764 [distribution_ release];
765 [type_ release];
766
767 if (description_ != nil)
768 [description_ release];
769 if (label_ != nil)
770 [label_ release];
771 if (origin_ != nil)
772 [origin_ release];
773 if (version_ != nil)
774 [version_ release];
775 if (defaultIcon_ != nil)
776 [defaultIcon_ release];
777
778 [super dealloc];
779 }
780
781 - (Source *) initWithMetaIndex:(metaIndex *)index {
782 if ((self = [super init]) != nil) {
783 trusted_ = index->IsTrusted();
784
785 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
786 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
787 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
788
789 description_ = nil;
790 label_ = nil;
791 origin_ = nil;
792 version_ = nil;
793 defaultIcon_ = nil;
794
795 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
796 if (dindex != NULL) {
797 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
798 std::string line;
799 while (std::getline(release, line)) {
800 std::string::size_type colon(line.find(':'));
801 if (colon == std::string::npos)
802 continue;
803
804 std::string name(line.substr(0, colon));
805 std::string value(line.substr(colon + 1));
806 while (!value.empty() && value[0] == ' ')
807 value = value.substr(1);
808
809 if (name == "Default-Icon")
810 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
811 else if (name == "Description")
812 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
813 else if (name == "Label")
814 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
815 else if (name == "Origin")
816 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
817 else if (name == "Version")
818 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
819 }
820 }
821 } return self;
822 }
823
824 - (BOOL) trusted {
825 return trusted_;
826 }
827
828 - (NSString *) uri {
829 return uri_;
830 }
831
832 - (NSString *) distribution {
833 return distribution_;
834 }
835
836 - (NSString *) type {
837 return type_;
838 }
839
840 - (NSString *) description {
841 return description_;
842 }
843
844 - (NSString *) label {
845 return label_;
846 }
847
848 - (NSString *) origin {
849 return origin_;
850 }
851
852 - (NSString *) version {
853 return version_;
854 }
855
856 - (NSString *) defaultIcon {
857 return defaultIcon_;
858 }
859
860 @end
861 /* }}} */
862 /* Relationship Class {{{ */
863 @interface Relationship : NSObject {
864 NSString *type_;
865 NSString *id_;
866 }
867
868 - (NSString *) type;
869 - (NSString *) id;
870 - (NSString *) name;
871
872 @end
873
874 @implementation Relationship
875
876 - (void) dealloc {
877 [type_ release];
878 [id_ release];
879 [super dealloc];
880 }
881
882 - (NSString *) type {
883 return type_;
884 }
885
886 - (NSString *) id {
887 return id_;
888 }
889
890 - (NSString *) name {
891 _assert(false);
892 return nil;
893 }
894
895 @end
896 /* }}} */
897 /* Package Class {{{ */
898 NSString *Scour(const char *field, const char *begin, const char *end) {
899 size_t i(0), l(strlen(field));
900
901 for (;;) {
902 const char *name = begin + i;
903 const char *colon = name + l;
904 const char *value = colon + 1;
905
906 if (
907 value < end &&
908 *colon == ':' &&
909 memcmp(name, field, l) == 0
910 ) {
911 while (value != end && value[0] == ' ')
912 ++value;
913 const char *line = std::find(value, end, '\n');
914 while (line != value && line[-1] == ' ')
915 --line;
916
917 return [NSString stringWithUTF8Bytes:value length:(line - value)];
918 } else {
919 begin = std::find(begin, end, '\n');
920 if (begin == end)
921 return nil;
922 ++begin;
923 }
924 }
925 }
926
927 @interface Package : NSObject {
928 pkgCache::PkgIterator iterator_;
929 _transient Database *database_;
930 pkgCache::VerIterator version_;
931 pkgCache::VerFileIterator file_;
932
933 Source *source_;
934 bool cached_;
935
936 NSString *latest_;
937 NSString *installed_;
938
939 NSString *id_;
940 NSString *name_;
941 NSString *tagline_;
942 NSString *icon_;
943 NSString *website_;
944 Address *sponsor_;
945 Address *author_;
946 NSArray *tags_;
947
948 NSArray *relationships_;
949 }
950
951 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
952 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
953
954 - (pkgCache::PkgIterator) iterator;
955
956 - (NSString *) section;
957 - (Address *) maintainer;
958 - (size_t) size;
959 - (NSString *) description;
960 - (NSString *) index;
961
962 - (NSDate *) seen;
963
964 - (NSString *) latest;
965 - (NSString *) installed;
966
967 - (BOOL) valid;
968 - (BOOL) upgradableAndEssential:(BOOL)essential;
969 - (BOOL) essential;
970 - (BOOL) broken;
971 - (BOOL) visible;
972
973 - (BOOL) half;
974 - (BOOL) halfConfigured;
975 - (BOOL) halfInstalled;
976 - (BOOL) hasMode;
977 - (NSString *) mode;
978
979 - (NSString *) id;
980 - (NSString *) name;
981 - (NSString *) tagline;
982 - (NSString *) icon;
983 - (NSString *) website;
984 - (Address *) author;
985
986 - (NSArray *) relationships;
987
988 - (Source *) source;
989
990 - (BOOL) matches:(NSString *)text;
991
992 - (bool) hasUsefulPurpose;
993 - (BOOL) hasTag:(NSString *)tag;
994
995 - (NSComparisonResult) compareByName:(Package *)package;
996 - (NSComparisonResult) compareBySection:(Package *)package;
997 - (NSComparisonResult) compareBySectionAndName:(Package *)package;
998 - (NSComparisonResult) compareForChanges:(Package *)package;
999
1000 - (void) install;
1001 - (void) remove;
1002
1003 - (NSNumber *) isSearchedForBy:(NSString *)search;
1004 - (NSNumber *) isInstalledInSection:(NSString *)section;
1005 - (NSNumber *) isUninstalledInSection:(NSString *)section;
1006
1007 @end
1008
1009 @implementation Package
1010
1011 - (void) dealloc {
1012 if (source_ != nil)
1013 [source_ release];
1014
1015 [latest_ release];
1016 if (installed_ != nil)
1017 [installed_ release];
1018
1019 [id_ release];
1020 if (name_ != nil)
1021 [name_ release];
1022 [tagline_ release];
1023 if (icon_ != nil)
1024 [icon_ release];
1025 if (website_ != nil)
1026 [website_ release];
1027 if (sponsor_ != nil)
1028 [sponsor_ release];
1029 if (author_ != nil)
1030 [author_ release];
1031 if (tags_ != nil)
1032 [tags_ release];
1033
1034 if (relationships_ != nil)
1035 [relationships_ release];
1036
1037 [super dealloc];
1038 }
1039
1040 - (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1041 if ((self = [super init]) != nil) {
1042 iterator_ = iterator;
1043 database_ = database;
1044
1045 version_ = [database_ policy]->GetCandidateVer(iterator_);
1046 latest_ = version_.end() ? nil : [StripVersion([NSString stringWithUTF8String:version_.VerStr()]) retain];
1047
1048 if (!version_.end())
1049 file_ = version_.FileList();
1050 else {
1051 pkgCache &cache([database_ cache]);
1052 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1053 }
1054
1055 pkgCache::VerIterator current = iterator_.CurrentVer();
1056 installed_ = current.end() ? nil : [StripVersion([NSString stringWithUTF8String:current.VerStr()]) retain];
1057
1058 id_ = [[[NSString stringWithUTF8String:iterator_.Name()] lowercaseString] retain];
1059
1060 if (!file_.end()) {
1061 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1062
1063 const char *begin, *end;
1064 parser->GetRec(begin, end);
1065
1066 name_ = Scour("Name", begin, end);
1067 if (name_ != nil)
1068 name_ = [name_ retain];
1069 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
1070 icon_ = Scour("Icon", begin, end);
1071 if (icon_ != nil)
1072 icon_ = [icon_ retain];
1073 website_ = Scour("Homepage", begin, end);
1074 if (website_ == nil)
1075 website_ = Scour("Website", begin, end);
1076 if (website_ != nil)
1077 website_ = [website_ retain];
1078 NSString *sponsor = Scour("Sponsor", begin, end);
1079 if (sponsor != nil)
1080 sponsor_ = [[Address addressWithString:sponsor] retain];
1081 NSString *author = Scour("Author", begin, end);
1082 if (author != nil)
1083 author_ = [[Address addressWithString:author] retain];
1084 NSString *tags = Scour("Tag", begin, end);
1085 if (tags != nil)
1086 tags_ = [[tags componentsSeparatedByString:@", "] retain];
1087 }
1088
1089 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
1090 if (metadata == nil || [metadata count] == 0) {
1091 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
1092 now_, @"FirstSeen",
1093 nil];
1094
1095 [Packages_ setObject:metadata forKey:id_];
1096 Changed_ = true;
1097 }
1098 } return self;
1099 }
1100
1101 + (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
1102 return [[[Package alloc]
1103 initWithIterator:iterator
1104 database:database
1105 ] autorelease];
1106 }
1107
1108 - (pkgCache::PkgIterator) iterator {
1109 return iterator_;
1110 }
1111
1112 - (NSString *) section {
1113 const char *section = iterator_.Section();
1114 return section == NULL ? nil : [[NSString stringWithUTF8String:section] stringByReplacingCharacter:'_' withCharacter:' '];
1115 }
1116
1117 - (Address *) maintainer {
1118 if (file_.end())
1119 return nil;
1120 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1121 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
1122 }
1123
1124 - (size_t) size {
1125 return version_.end() ? 0 : version_->InstalledSize;
1126 }
1127
1128 - (NSString *) description {
1129 if (file_.end())
1130 return nil;
1131 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1132 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
1133
1134 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1135 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1136 if ([lines count] < 2)
1137 return nil;
1138
1139 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1140 for (size_t i(1); i != [lines count]; ++i) {
1141 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1142 [trimmed addObject:trim];
1143 }
1144
1145 return [trimmed componentsJoinedByString:@"\n"];
1146 }
1147
1148 - (NSString *) index {
1149 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1150 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1151 }
1152
1153 - (NSDate *) seen {
1154 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
1155 }
1156
1157 - (NSString *) latest {
1158 return latest_;
1159 }
1160
1161 - (NSString *) installed {
1162 return installed_;
1163 }
1164
1165 - (BOOL) valid {
1166 return !version_.end();
1167 }
1168
1169 - (BOOL) upgradableAndEssential:(BOOL)essential {
1170 pkgCache::VerIterator current = iterator_.CurrentVer();
1171
1172 if (current.end())
1173 return essential && [self essential];
1174 else {
1175 pkgCache::VerIterator candidate = [database_ policy]->GetCandidateVer(iterator_);
1176 return !candidate.end() && candidate != current;
1177 }
1178 }
1179
1180 - (BOOL) essential {
1181 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
1182 }
1183
1184 - (BOOL) broken {
1185 return [database_ cache][iterator_].InstBroken();
1186 }
1187
1188 - (BOOL) visible {
1189 NSString *section = [self section];
1190 return [self hasUsefulPurpose] && (section == nil || isSectionVisible(section));
1191 }
1192
1193 - (BOOL) half {
1194 unsigned char current = iterator_->CurrentState;
1195 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1196 }
1197
1198 - (BOOL) halfConfigured {
1199 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1200 }
1201
1202 - (BOOL) halfInstalled {
1203 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1204 }
1205
1206 - (BOOL) hasMode {
1207 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1208 return state.Mode != pkgDepCache::ModeKeep;
1209 }
1210
1211 - (NSString *) mode {
1212 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1213
1214 switch (state.Mode) {
1215 case pkgDepCache::ModeDelete:
1216 if ((state.iFlags & pkgDepCache::Purge) != 0)
1217 return @"Purge";
1218 else
1219 return @"Remove";
1220 _assert(false);
1221 case pkgDepCache::ModeKeep:
1222 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1223 return nil;
1224 else
1225 return nil;
1226 _assert(false);
1227 case pkgDepCache::ModeInstall:
1228 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1229 return @"Reinstall";
1230 else switch (state.Status) {
1231 case -1:
1232 return @"Downgrade";
1233 case 0:
1234 return @"Install";
1235 case 1:
1236 return @"Upgrade";
1237 case 2:
1238 return @"New Install";
1239 default:
1240 _assert(false);
1241 }
1242 default:
1243 _assert(false);
1244 }
1245 }
1246
1247 - (NSString *) id {
1248 return id_;
1249 }
1250
1251 - (NSString *) name {
1252 return name_ == nil ? id_ : name_;
1253 }
1254
1255 - (NSString *) tagline {
1256 return tagline_;
1257 }
1258
1259 - (NSString *) icon {
1260 return icon_;
1261 }
1262
1263 - (NSString *) website {
1264 return website_;
1265 }
1266
1267 - (Address *) sponsor {
1268 return sponsor_;
1269 }
1270
1271 - (Address *) author {
1272 return author_;
1273 }
1274
1275 - (NSArray *) relationships {
1276 return relationships_;
1277 }
1278
1279 - (Source *) source {
1280 if (!cached_) {
1281 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
1282 cached_ = true;
1283 }
1284
1285 return source_;
1286 }
1287
1288 - (BOOL) matches:(NSString *)text {
1289 if (text == nil)
1290 return NO;
1291
1292 NSRange range;
1293
1294 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1295 if (range.location != NSNotFound)
1296 return YES;
1297
1298 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1299 if (range.location != NSNotFound)
1300 return YES;
1301
1302 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1303 if (range.location != NSNotFound)
1304 return YES;
1305
1306 return NO;
1307 }
1308
1309 - (bool) hasUsefulPurpose {
1310 if (tags_ == nil)
1311 return true;
1312 bool purpose(false);
1313
1314 for (int i(0), e([tags_ count]); i != e; ++i) {
1315 NSString *tag = [tags_ objectAtIndex:i];
1316 if ([tag hasPrefix:@"purpose::"]) {
1317 bool purpose(false);
1318 if ([tag isEqualToString:@"purpose::console"]) {
1319 return true;
1320 } else if ([tag isEqualToString:@"purpose::library"]) {
1321 return true;
1322 } else if ([tag isEqualToString:@"purpose::x"]) {
1323 return true;
1324 } else
1325 purpose = true;
1326 }
1327 }
1328
1329 return !purpose;
1330 }
1331
1332 - (BOOL) hasTag:(NSString *)tag {
1333 return tags_ == nil ? NO : [tags_ containsObject:tag];
1334 }
1335
1336 - (NSComparisonResult) compareByName:(Package *)package {
1337 NSString *lhs = [self name];
1338 NSString *rhs = [package name];
1339
1340 if ([lhs length] != 0 && [rhs length] != 0) {
1341 unichar lhc = [lhs characterAtIndex:0];
1342 unichar rhc = [rhs characterAtIndex:0];
1343
1344 if (isalpha(lhc) && !isalpha(rhc))
1345 return NSOrderedAscending;
1346 else if (!isalpha(lhc) && isalpha(rhc))
1347 return NSOrderedDescending;
1348 }
1349
1350 return [lhs caseInsensitiveCompare:rhs];
1351 }
1352
1353 - (NSComparisonResult) compareBySection:(Package *)package {
1354 NSString *lhs = [self section];
1355 NSString *rhs = [package section];
1356
1357 if (lhs == NULL && rhs != NULL)
1358 return NSOrderedAscending;
1359 else if (lhs != NULL && rhs == NULL)
1360 return NSOrderedDescending;
1361 else if (lhs != NULL && rhs != NULL) {
1362 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
1363 if (result != NSOrderedSame)
1364 return result;
1365 }
1366
1367 return NSOrderedSame;
1368 }
1369
1370 - (NSComparisonResult) compareBySectionAndName:(Package *)package {
1371 NSString *lhs = [self section];
1372 NSString *rhs = [package section];
1373
1374 if (lhs == NULL && rhs != NULL)
1375 return NSOrderedAscending;
1376 else if (lhs != NULL && rhs == NULL)
1377 return NSOrderedDescending;
1378 else if (lhs != NULL && rhs != NULL) {
1379 NSComparisonResult result = [lhs compare:rhs];
1380 if (result != NSOrderedSame)
1381 return result;
1382 }
1383
1384 return [self compareByName:package];
1385 }
1386
1387 - (NSComparisonResult) compareForChanges:(Package *)package {
1388 BOOL lhs = [self upgradableAndEssential:YES];
1389 BOOL rhs = [package upgradableAndEssential:YES];
1390
1391 if (lhs != rhs)
1392 return lhs ? NSOrderedAscending : NSOrderedDescending;
1393 else if (!lhs) {
1394 switch ([[self seen] compare:[package seen]]) {
1395 case NSOrderedAscending:
1396 return NSOrderedDescending;
1397 case NSOrderedSame:
1398 break;
1399 case NSOrderedDescending:
1400 return NSOrderedAscending;
1401 default:
1402 _assert(false);
1403 }
1404 }
1405
1406 return [self compareByName:package];
1407 }
1408
1409 - (void) install {
1410 pkgProblemResolver *resolver = [database_ resolver];
1411 resolver->Clear(iterator_);
1412 resolver->Protect(iterator_);
1413 pkgCacheFile &cache([database_ cache]);
1414 cache->MarkInstall(iterator_, false);
1415 pkgDepCache::StateCache &state((*cache)[iterator_]);
1416 if (!state.Install())
1417 cache->SetReInstall(iterator_, true);
1418 }
1419
1420 - (void) remove {
1421 pkgProblemResolver *resolver = [database_ resolver];
1422 resolver->Clear(iterator_);
1423 resolver->Protect(iterator_);
1424 resolver->Remove(iterator_);
1425 [database_ cache]->MarkDelete(iterator_, true);
1426 }
1427
1428 - (NSNumber *) isSearchedForBy:(NSString *)search {
1429 return [NSNumber numberWithBool:([self valid] && [self matches:search])];
1430 }
1431
1432 - (NSNumber *) isInstalledInSection:(NSString *)section {
1433 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1434 }
1435
1436 - (NSNumber *) isUninstalledInSection:(NSString *)name {
1437 NSString *section = [self section];
1438
1439 return [NSNumber numberWithBool:([self valid] && [self installed] == nil && (
1440 (name == nil && [self visible] ||
1441 section == nil && [name length] == 0 ||
1442 [name isEqualToString:section])
1443 ))];
1444 }
1445
1446 @end
1447 /* }}} */
1448 /* Section Class {{{ */
1449 @interface Section : NSObject {
1450 NSString *name_;
1451 size_t row_;
1452 size_t count_;
1453 }
1454
1455 - (NSComparisonResult) compareByName:(Section *)section;
1456 - (Section *) initWithName:(NSString *)name;
1457 - (Section *) initWithName:(NSString *)name row:(size_t)row;
1458 - (NSString *) name;
1459 - (size_t) row;
1460 - (size_t) count;
1461 - (void) addToCount;
1462
1463 @end
1464
1465 @implementation Section
1466
1467 - (void) dealloc {
1468 [name_ release];
1469 [super dealloc];
1470 }
1471
1472 - (NSComparisonResult) compareByName:(Section *)section {
1473 NSString *lhs = [self name];
1474 NSString *rhs = [section name];
1475
1476 if ([lhs length] != 0 && [rhs length] != 0) {
1477 unichar lhc = [lhs characterAtIndex:0];
1478 unichar rhc = [rhs characterAtIndex:0];
1479
1480 if (isalpha(lhc) && !isalpha(rhc))
1481 return NSOrderedAscending;
1482 else if (!isalpha(lhc) && isalpha(rhc))
1483 return NSOrderedDescending;
1484 }
1485
1486 return [lhs caseInsensitiveCompare:rhs];
1487 }
1488
1489 - (Section *) initWithName:(NSString *)name {
1490 return [self initWithName:name row:0];
1491 }
1492
1493 - (Section *) initWithName:(NSString *)name row:(size_t)row {
1494 if ((self = [super init]) != nil) {
1495 name_ = [name retain];
1496 row_ = row;
1497 } return self;
1498 }
1499
1500 - (NSString *) name {
1501 return name_;
1502 }
1503
1504 - (size_t) row {
1505 return row_;
1506 }
1507
1508 - (size_t) count {
1509 return count_;
1510 }
1511
1512 - (void) addToCount {
1513 ++count_;
1514 }
1515
1516 @end
1517 /* }}} */
1518
1519 /* Database Implementation {{{ */
1520 @implementation Database
1521
1522 - (void) dealloc {
1523 _assert(false);
1524 [super dealloc];
1525 }
1526
1527 - (void) _readCydia:(NSNumber *)fd {
1528 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1529
1530 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1531 std::istream is(&ib);
1532 std::string line;
1533
1534 while (std::getline(is, line)) {
1535 const char *data(line.c_str());
1536 //size_t size = line.size();
1537 fprintf(stderr, "C:%s\n", data);
1538 }
1539
1540 [pool release];
1541 _assert(false);
1542 }
1543
1544 - (void) _readStatus:(NSNumber *)fd {
1545 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1546
1547 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1548 std::istream is(&ib);
1549 std::string line;
1550
1551 Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
1552 Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
1553
1554 while (std::getline(is, line)) {
1555 const char *data(line.c_str());
1556 size_t size = line.size();
1557 fprintf(stderr, "S:%s\n", data);
1558
1559 if (conffile_r(data, size)) {
1560 [delegate_ setConfigurationData:conffile_r[1]];
1561 } else if (strncmp(data, "status: ", 8) == 0) {
1562 NSString *string = [NSString stringWithUTF8String:(data + 8)];
1563 [delegate_ setProgressTitle:string];
1564 } else if (pmstatus_r(data, size)) {
1565 std::string type([pmstatus_r[1] UTF8String]);
1566 NSString *id = pmstatus_r[2];
1567
1568 float percent([pmstatus_r[3] floatValue]);
1569 [delegate_ setProgressPercent:(percent / 100)];
1570
1571 NSString *string = pmstatus_r[4];
1572
1573 if (type == "pmerror")
1574 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1575 withObject:[NSArray arrayWithObjects:string, id, nil]
1576 waitUntilDone:YES
1577 ];
1578 else if (type == "pmstatus")
1579 [delegate_ setProgressTitle:string];
1580 else if (type == "pmconffile")
1581 [delegate_ setConfigurationData:string];
1582 else _assert(false);
1583 } else _assert(false);
1584 }
1585
1586 [pool release];
1587 _assert(false);
1588 }
1589
1590 - (void) _readOutput:(NSNumber *)fd {
1591 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1592
1593 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1594 std::istream is(&ib);
1595 std::string line;
1596
1597 while (std::getline(is, line)) {
1598 fprintf(stderr, "O:%s\n", line.c_str());
1599 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
1600 }
1601
1602 [pool release];
1603 _assert(false);
1604 }
1605
1606 - (FILE *) input {
1607 return input_;
1608 }
1609
1610 - (Package *) packageWithName:(NSString *)name {
1611 if (static_cast<pkgDepCache *>(cache_) == NULL)
1612 return nil;
1613 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1614 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1615 }
1616
1617 - (Database *) init {
1618 if ((self = [super init]) != nil) {
1619 policy_ = NULL;
1620 records_ = NULL;
1621 resolver_ = NULL;
1622 fetcher_ = NULL;
1623 lock_ = NULL;
1624
1625 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1626 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
1627
1628 int fds[2];
1629
1630 _assert(pipe(fds) != -1);
1631 cydiafd_ = fds[1];
1632
1633 _config->Set("APT::Keep-Fds::", cydiafd_);
1634 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 0"] UTF8String], _not(int));
1635
1636 [NSThread
1637 detachNewThreadSelector:@selector(_readCydia:)
1638 toTarget:self
1639 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1640 ];
1641
1642 _assert(pipe(fds) != -1);
1643 statusfd_ = fds[1];
1644
1645 [NSThread
1646 detachNewThreadSelector:@selector(_readStatus:)
1647 toTarget:self
1648 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1649 ];
1650
1651 _assert(pipe(fds) != -1);
1652 _assert(dup2(fds[0], 0) != -1);
1653 _assert(close(fds[0]) != -1);
1654
1655 input_ = fdopen(fds[1], "a");
1656
1657 _assert(pipe(fds) != -1);
1658 _assert(dup2(fds[1], 1) != -1);
1659 _assert(close(fds[1]) != -1);
1660
1661 [NSThread
1662 detachNewThreadSelector:@selector(_readOutput:)
1663 toTarget:self
1664 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1665 ];
1666 } return self;
1667 }
1668
1669 - (pkgCacheFile &) cache {
1670 return cache_;
1671 }
1672
1673 - (pkgDepCache::Policy *) policy {
1674 return policy_;
1675 }
1676
1677 - (pkgRecords *) records {
1678 return records_;
1679 }
1680
1681 - (pkgProblemResolver *) resolver {
1682 return resolver_;
1683 }
1684
1685 - (pkgAcquire &) fetcher {
1686 return *fetcher_;
1687 }
1688
1689 - (NSArray *) packages {
1690 return packages_;
1691 }
1692
1693 - (void) reloadData {
1694 _error->Discard();
1695
1696 delete list_;
1697 list_ = NULL;
1698 manager_ = NULL;
1699 delete lock_;
1700 lock_ = NULL;
1701 delete fetcher_;
1702 fetcher_ = NULL;
1703 delete resolver_;
1704 resolver_ = NULL;
1705 delete records_;
1706 records_ = NULL;
1707 delete policy_;
1708 policy_ = NULL;
1709
1710 cache_.Close();
1711
1712 if (!cache_.Open(progress_, true)) {
1713 std::string error;
1714 if (!_error->PopMessage(error))
1715 _assert(false);
1716 _error->Discard();
1717 fprintf(stderr, "cache_.Open():[%s]\n", error.c_str());
1718
1719 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
1720 [delegate_ repairWithSelector:@selector(configure)];
1721 else if (error == "The package lists or status file could not be parsed or opened.")
1722 [delegate_ repairWithSelector:@selector(update)];
1723 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
1724 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
1725 // else if (error == "The list of sources could not be read.")
1726 else _assert(false);
1727
1728 return;
1729 }
1730
1731 now_ = [[NSDate date] retain];
1732
1733 policy_ = new pkgDepCache::Policy();
1734 records_ = new pkgRecords(cache_);
1735 resolver_ = new pkgProblemResolver(cache_);
1736 fetcher_ = new pkgAcquire(&status_);
1737 lock_ = NULL;
1738
1739 list_ = new pkgSourceList();
1740 _assert(list_->ReadMainList());
1741
1742 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1743 _assert(pkgApplyStatus(cache_));
1744
1745 if (cache_->BrokenCount() != 0) {
1746 _assert(pkgFixBroken(cache_));
1747 _assert(cache_->BrokenCount() == 0);
1748 _assert(pkgMinimizeUpgrade(cache_));
1749 }
1750
1751 [sources_ removeAllObjects];
1752 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1753 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1754 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1755 [sources_
1756 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1757 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1758 ];
1759 }
1760
1761 [packages_ removeAllObjects];
1762 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1763 if (Package *package = [Package packageWithIterator:iterator database:self])
1764 [packages_ addObject:package];
1765
1766 [packages_ sortUsingSelector:@selector(compareByName:)];
1767 }
1768
1769 - (void) configure {
1770 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
1771 system([dpkg UTF8String]);
1772 }
1773
1774 - (void) clean {
1775 if (lock_ != NULL)
1776 return;
1777
1778 FileFd Lock;
1779 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1780 _assert(!_error->PendingError());
1781
1782 pkgAcquire fetcher;
1783 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
1784
1785 class LogCleaner :
1786 public pkgArchiveCleaner
1787 {
1788 protected:
1789 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
1790 unlink(File);
1791 }
1792 } cleaner;
1793
1794 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
1795 std::string error;
1796 while (_error->PopMessage(error))
1797 fprintf(stderr, "ArchiveCleaner: %s\n", error.c_str());
1798 }
1799 }
1800
1801 - (void) prepare {
1802 pkgRecords records(cache_);
1803
1804 lock_ = new FileFd();
1805 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1806 _assert(!_error->PendingError());
1807
1808 pkgSourceList list;
1809 // XXX: explain this with an error message
1810 _assert(list.ReadMainList());
1811
1812 manager_ = (_system->CreatePM(cache_));
1813 _assert(manager_->GetArchives(fetcher_, &list, &records));
1814 _assert(!_error->PendingError());
1815 }
1816
1817 - (void) perform {
1818 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1819 pkgSourceList list;
1820 _assert(list.ReadMainList());
1821 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1822 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1823 }
1824
1825 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
1826 _trace();
1827 return;
1828 }
1829
1830 bool failed = false;
1831 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
1832 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
1833 continue;
1834
1835 std::string uri = (*item)->DescURI();
1836 std::string error = (*item)->ErrorText;
1837
1838 fprintf(stderr, "pAf:%s:%s\n", uri.c_str(), error.c_str());
1839 failed = true;
1840
1841 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
1842 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
1843 waitUntilDone:YES
1844 ];
1845 }
1846
1847 if (failed) {
1848 _trace();
1849 return;
1850 }
1851
1852 _system->UnLock();
1853 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1854
1855 if (_error->PendingError()) {
1856 _trace();
1857 return;
1858 }
1859
1860 if (result == pkgPackageManager::Failed) {
1861 _trace();
1862 return;
1863 }
1864
1865 if (result != pkgPackageManager::Completed) {
1866 _trace();
1867 return;
1868 }
1869
1870 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1871 pkgSourceList list;
1872 _assert(list.ReadMainList());
1873 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1874 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1875 }
1876
1877 if (![before isEqualToArray:after])
1878 [self update];
1879 }
1880
1881 - (void) upgrade {
1882 _assert(pkgDistUpgrade(cache_));
1883 }
1884
1885 - (void) update {
1886 [self updateWithStatus:status_];
1887 }
1888
1889 - (void) updateWithStatus:(Status &)status {
1890 pkgSourceList list;
1891 _assert(list.ReadMainList());
1892
1893 FileFd lock;
1894 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1895 _assert(!_error->PendingError());
1896
1897 pkgAcquire fetcher(&status);
1898 _assert(list.GetIndexes(&fetcher));
1899
1900 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1901 bool failed = false;
1902 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1903 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1904 (*item)->Finished();
1905 failed = true;
1906 }
1907
1908 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1909 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1910 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
1911 }
1912
1913 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
1914 Changed_ = true;
1915 }
1916 }
1917
1918 - (void) setDelegate:(id)delegate {
1919 delegate_ = delegate;
1920 status_.setDelegate(delegate);
1921 progress_.setDelegate(delegate);
1922 }
1923
1924 - (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1925 pkgIndexFile *index(NULL);
1926 list_->FindIndex(file, index);
1927 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
1928 }
1929
1930 @end
1931 /* }}} */
1932
1933 /* Confirmation View {{{ */
1934 void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1935 if ([packages count] == 0)
1936 return;
1937
1938 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1939 [fields setObject:text forKey:key];
1940
1941 CGColor blue(space_, 0, 0, 0.4, 1);
1942 [text setTextColor:blue];
1943 }
1944
1945 @protocol ConfirmationViewDelegate
1946 - (void) cancel;
1947 - (void) confirm;
1948 @end
1949
1950 @interface ConfirmationView : UIView {
1951 Database *database_;
1952 id delegate_;
1953 UITransitionView *transition_;
1954 UIView *overlay_;
1955 UINavigationBar *navbar_;
1956 UIPreferencesTable *table_;
1957 NSMutableDictionary *fields_;
1958 UIAlertSheet *essential_;
1959 }
1960
1961 - (void) cancel;
1962
1963 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
1964
1965 @end
1966
1967 @implementation ConfirmationView
1968
1969 - (void) dealloc {
1970 [navbar_ setDelegate:nil];
1971 [transition_ setDelegate:nil];
1972 [table_ setDataSource:nil];
1973
1974 [transition_ release];
1975 [overlay_ release];
1976 [navbar_ release];
1977 [table_ release];
1978 [fields_ release];
1979 if (essential_ != nil)
1980 [essential_ release];
1981 [super dealloc];
1982 }
1983
1984 - (void) cancel {
1985 [transition_ transition:7 toView:nil];
1986 [delegate_ cancel];
1987 }
1988
1989 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1990 if (from != nil && to == nil)
1991 [self removeFromSuperview];
1992 }
1993
1994 - (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1995 switch (button) {
1996 case 0:
1997 if (essential_ != nil)
1998 [essential_ popupAlertAnimated:YES];
1999 else
2000 [delegate_ confirm];
2001 break;
2002
2003 case 1:
2004 [self cancel];
2005 break;
2006 }
2007 }
2008
2009 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2010 NSString *context = [sheet context];
2011
2012 if ([context isEqualToString:@"remove"])
2013 switch (button) {
2014 case 1:
2015 [self cancel];
2016 break;
2017 case 2:
2018 [delegate_ confirm];
2019 break;
2020 default:
2021 _assert(false);
2022 }
2023 else if ([context isEqualToString:@"unable"])
2024 [self cancel];
2025
2026 [sheet dismiss];
2027 }
2028
2029 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2030 return 2;
2031 }
2032
2033 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2034 switch (group) {
2035 case 0: return @"Statistics";
2036 case 1: return @"Modifications";
2037
2038 default: _assert(false);
2039 }
2040 }
2041
2042 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2043 switch (group) {
2044 case 0: return 3;
2045 case 1: return [fields_ count];
2046
2047 default: _assert(false);
2048 }
2049 }
2050
2051 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2052 if (group != 1 || row == -1)
2053 return proposed;
2054 else {
2055 _assert(size_t(row) < [fields_ count]);
2056 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
2057 }
2058 }
2059
2060 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2061 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2062 [cell setShowSelection:NO];
2063
2064 switch (group) {
2065 case 0: switch (row) {
2066 case 0: {
2067 [cell setTitle:@"Downloading"];
2068 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
2069 } break;
2070
2071 case 1: {
2072 [cell setTitle:@"Resuming At"];
2073 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
2074 } break;
2075
2076 case 2: {
2077 double size([database_ cache]->UsrSize());
2078
2079 if (size < 0) {
2080 [cell setTitle:@"Disk Freeing"];
2081 [cell setValue:SizeString(-size)];
2082 } else {
2083 [cell setTitle:@"Disk Using"];
2084 [cell setValue:SizeString(size)];
2085 }
2086 } break;
2087
2088 default: _assert(false);
2089 } break;
2090
2091 case 1:
2092 _assert(size_t(row) < [fields_ count]);
2093 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
2094 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
2095 break;
2096
2097 default: _assert(false);
2098 }
2099
2100 return cell;
2101 }
2102
2103 - (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
2104 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2105 database_ = database;
2106 delegate_ = delegate;
2107
2108 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2109 [self addSubview:transition_];
2110
2111 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2112
2113 CGSize navsize = [UINavigationBar defaultSize];
2114 CGRect navrect = {{0, 0}, navsize};
2115 CGRect bounds = [overlay_ bounds];
2116
2117 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2118 if (Advanced_)
2119 [navbar_ setBarStyle:1];
2120 [navbar_ setDelegate:self];
2121
2122 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
2123 [navbar_ pushNavigationItem:navitem];
2124 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
2125
2126 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2127
2128 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2129 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2130 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2131 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2132 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
2133
2134 bool remove(false);
2135
2136 pkgCacheFile &cache([database_ cache]);
2137 NSArray *packages = [database_ packages];
2138 for (size_t i(0), e = [packages count]; i != e; ++i) {
2139 Package *package = [packages objectAtIndex:i];
2140 pkgCache::PkgIterator iterator = [package iterator];
2141 pkgDepCache::StateCache &state(cache[iterator]);
2142
2143 NSString *name([package name]);
2144
2145 if (state.NewInstall())
2146 [installing addObject:name];
2147 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2148 [reinstalling addObject:name];
2149 else if (state.Upgrade())
2150 [upgrading addObject:name];
2151 else if (state.Downgrade())
2152 [downgrading addObject:name];
2153 else if (state.Delete()) {
2154 if ([package essential])
2155 remove = true;
2156 [removing addObject:name];
2157 }
2158 }
2159
2160 if (!remove)
2161 essential_ = nil;
2162 else if (Advanced_ || true) {
2163 essential_ = [[UIAlertSheet alloc]
2164 initWithTitle:@"Removing Essentials"
2165 buttons:[NSArray arrayWithObjects:
2166 @"Cancel Operation (Safe)",
2167 @"Force Removal (Unsafe)",
2168 nil]
2169 defaultButtonIndex:0
2170 delegate:self
2171 context:@"remove"
2172 ];
2173
2174 #ifndef __OBJC2__
2175 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
2176 #endif
2177 [essential_ setBodyText:@"This operation involves the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. If you continue, you may not be able to use Cydia to repair any damage."];
2178 } else {
2179 essential_ = [[UIAlertSheet alloc]
2180 initWithTitle:@"Unable to Comply"
2181 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2182 defaultButtonIndex:0
2183 delegate:self
2184 context:@"unable"
2185 ];
2186
2187 [essential_ setBodyText:@"This operation requires the removal of one or more packages that are required for the continued operation of either Cydia or iPhoneOS. In order to continue and force this operation you will need to be activate the Advanced mode under to continue and force this operation you will need to be activate the Advanced mode under Settings."];
2188 }
2189
2190 AddTextView(fields_, installing, @"Installing");
2191 AddTextView(fields_, reinstalling, @"Reinstalling");
2192 AddTextView(fields_, upgrading, @"Upgrading");
2193 AddTextView(fields_, downgrading, @"Downgrading");
2194 AddTextView(fields_, removing, @"Removing");
2195
2196 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2197 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2198 )];
2199
2200 [table_ setReusesTableCells:YES];
2201 [table_ setDataSource:self];
2202 [table_ reloadData];
2203
2204 [overlay_ addSubview:navbar_];
2205 [overlay_ addSubview:table_];
2206
2207 [view addSubview:self];
2208
2209 [transition_ setDelegate:self];
2210
2211 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2212 [transition_ transition:0 toView:blank];
2213 [transition_ transition:3 toView:overlay_];
2214 } return self;
2215 }
2216
2217 @end
2218 /* }}} */
2219
2220 /* Progress Data {{{ */
2221 @interface ProgressData : NSObject {
2222 SEL selector_;
2223 id target_;
2224 id object_;
2225 }
2226
2227 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
2228
2229 - (SEL) selector;
2230 - (id) target;
2231 - (id) object;
2232 @end
2233
2234 @implementation ProgressData
2235
2236 - (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2237 if ((self = [super init]) != nil) {
2238 selector_ = selector;
2239 target_ = target;
2240 object_ = object;
2241 } return self;
2242 }
2243
2244 - (SEL) selector {
2245 return selector_;
2246 }
2247
2248 - (id) target {
2249 return target_;
2250 }
2251
2252 - (id) object {
2253 return object_;
2254 }
2255
2256 @end
2257 /* }}} */
2258 /* Progress View {{{ */
2259 Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
2260
2261 @interface ProgressView : UIView <
2262 ConfigurationDelegate,
2263 ProgressDelegate
2264 > {
2265 _transient Database *database_;
2266 UIView *view_;
2267 UIView *background_;
2268 UITransitionView *transition_;
2269 UIView *overlay_;
2270 UINavigationBar *navbar_;
2271 UIProgressBar *progress_;
2272 UITextView *output_;
2273 UITextLabel *status_;
2274 UIPushButton *close_;
2275 id delegate_;
2276 BOOL running_;
2277 }
2278
2279 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2280
2281 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
2282 - (void) setContentView:(UIView *)view;
2283 - (void) resetView;
2284
2285 - (void) _retachThread;
2286 - (void) _detachNewThreadData:(ProgressData *)data;
2287 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2288
2289 - (BOOL) isRunning;
2290
2291 @end
2292
2293 @protocol ProgressViewDelegate
2294 - (void) progressViewIsComplete:(ProgressView *)sender;
2295 @end
2296
2297 @implementation ProgressView
2298
2299 - (void) dealloc {
2300 [transition_ setDelegate:nil];
2301 [navbar_ setDelegate:nil];
2302
2303 [view_ release];
2304 if (background_ != nil)
2305 [background_ release];
2306 [transition_ release];
2307 [overlay_ release];
2308 [navbar_ release];
2309 [progress_ release];
2310 [output_ release];
2311 [status_ release];
2312 [close_ release];
2313 [super dealloc];
2314 }
2315
2316 - (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2317 if (bootstrap_ && from == overlay_ && to == view_)
2318 exit(0);
2319 }
2320
2321 - (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
2322 if ((self = [super initWithFrame:frame]) != nil) {
2323 database_ = database;
2324 delegate_ = delegate;
2325
2326 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2327 [transition_ setDelegate:self];
2328
2329 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2330
2331 if (bootstrap_)
2332 [overlay_ setBackgroundColor:Black_];
2333 else {
2334 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2335 [background_ setBackgroundColor:Black_];
2336 [self addSubview:background_];
2337 }
2338
2339 [self addSubview:transition_];
2340
2341 CGSize navsize = [UINavigationBar defaultSize];
2342 CGRect navrect = {{0, 0}, navsize};
2343
2344 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2345 [overlay_ addSubview:navbar_];
2346
2347 [navbar_ setBarStyle:1];
2348 [navbar_ setDelegate:self];
2349
2350 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2351 [navbar_ pushNavigationItem:navitem];
2352
2353 CGRect bounds = [overlay_ bounds];
2354 CGSize prgsize = [UIProgressBar defaultSize];
2355
2356 CGRect prgrect = {{
2357 (bounds.size.width - prgsize.width) / 2,
2358 bounds.size.height - prgsize.height - 20
2359 }, prgsize};
2360
2361 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2362 [progress_ setStyle:0];
2363
2364 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2365 10,
2366 bounds.size.height - prgsize.height - 50,
2367 bounds.size.width - 20,
2368 24
2369 )];
2370
2371 [status_ setColor:White_];
2372 [status_ setBackgroundColor:Clear_];
2373
2374 [status_ setCentersHorizontally:YES];
2375 //[status_ setFont:font];
2376
2377 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2378 10,
2379 navrect.size.height + 20,
2380 bounds.size.width - 20,
2381 bounds.size.height - navsize.height - 62 - navrect.size.height
2382 )];
2383
2384 //[output_ setTextFont:@"Courier New"];
2385 [output_ setTextSize:12];
2386
2387 [output_ setTextColor:White_];
2388 [output_ setBackgroundColor:Clear_];
2389
2390 [output_ setMarginTop:0];
2391 [output_ setAllowsRubberBanding:YES];
2392 [output_ setEditable:NO];
2393
2394 [overlay_ addSubview:output_];
2395
2396 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2397 10,
2398 bounds.size.height - prgsize.height - 50,
2399 bounds.size.width - 20,
2400 32 + prgsize.height
2401 )];
2402
2403 [close_ setAutosizesToFit:NO];
2404 [close_ setDrawsShadow:YES];
2405 [close_ setStretchBackground:YES];
2406 [close_ setTitle:@"Close Window"];
2407 [close_ setEnabled:YES];
2408
2409 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2410 [close_ setTitleFont:bold];
2411 CFRelease(bold);
2412
2413 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2414 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2415 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
2416 } return self;
2417 }
2418
2419 - (void) setContentView:(UIView *)view {
2420 view_ = [view retain];
2421 }
2422
2423 - (void) resetView {
2424 [transition_ transition:6 toView:view_];
2425 }
2426
2427 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2428 NSString *context = [sheet context];
2429 if ([context isEqualToString:@"conffile"]) {
2430 FILE *input = [database_ input];
2431
2432 switch (button) {
2433 case 1:
2434 fprintf(input, "N\n");
2435 fflush(input);
2436 break;
2437 case 2:
2438 fprintf(input, "Y\n");
2439 fflush(input);
2440 break;
2441 default:
2442 _assert(false);
2443 }
2444 }
2445
2446 [sheet dismiss];
2447 }
2448
2449 - (void) closeButtonPushed {
2450 [delegate_ progressViewIsComplete:self];
2451 [self resetView];
2452 }
2453
2454 - (void) _retachThread {
2455 UINavigationItem *item = [navbar_ topItem];
2456 [item setTitle:@"Complete"];
2457
2458 [overlay_ addSubview:close_];
2459 [progress_ removeFromSuperview];
2460 [status_ removeFromSuperview];
2461
2462 #ifdef __OBJC2__
2463 notify_post("com.apple.mobile.application_installed");
2464 #endif
2465
2466 [delegate_ setStatusBarShowsProgress:NO];
2467
2468 running_ = NO;
2469 }
2470
2471 - (void) _detachNewThreadData:(ProgressData *)data {
2472 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2473
2474 [[data target] performSelector:[data selector] withObject:[data object]];
2475 [data release];
2476
2477 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
2478
2479 [pool release];
2480 }
2481
2482 - (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2483 UINavigationItem *item = [navbar_ topItem];
2484 [item setTitle:title];
2485
2486 [status_ setText:nil];
2487 [output_ setText:@""];
2488 [progress_ setProgress:0];
2489
2490 [close_ removeFromSuperview];
2491 [overlay_ addSubview:progress_];
2492 [overlay_ addSubview:status_];
2493
2494 [delegate_ setStatusBarShowsProgress:YES];
2495 running_ = YES;
2496
2497 [transition_ transition:6 toView:overlay_];
2498
2499 [NSThread
2500 detachNewThreadSelector:@selector(_detachNewThreadData:)
2501 toTarget:self
2502 withObject:[[ProgressData alloc]
2503 initWithSelector:selector
2504 target:target
2505 object:object
2506 ]
2507 ];
2508 }
2509
2510 - (void) repairWithSelector:(SEL)selector {
2511 [self
2512 detachNewThreadSelector:selector
2513 toTarget:database_
2514 withObject:nil
2515 title:@"Repairing..."
2516 ];
2517 }
2518
2519 - (void) setConfigurationData:(NSString *)data {
2520 [self
2521 performSelectorOnMainThread:@selector(_setConfigurationData:)
2522 withObject:data
2523 waitUntilDone:YES
2524 ];
2525 }
2526
2527 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2528 Package *package = id == nil ? nil : [database_ packageWithName:id];
2529
2530 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2531 initWithTitle:(package == nil ? @"Source Error" : [package name])
2532 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2533 defaultButtonIndex:0
2534 delegate:self
2535 context:@"error"
2536 ] autorelease];
2537
2538 [sheet setBodyText:error];
2539 [sheet popupAlertAnimated:YES];
2540 }
2541
2542 - (void) setProgressTitle:(NSString *)title {
2543 [self
2544 performSelectorOnMainThread:@selector(_setProgressTitle:)
2545 withObject:title
2546 waitUntilDone:YES
2547 ];
2548 }
2549
2550 - (void) setProgressPercent:(float)percent {
2551 [self
2552 performSelectorOnMainThread:@selector(_setProgressPercent:)
2553 withObject:[NSNumber numberWithFloat:percent]
2554 waitUntilDone:YES
2555 ];
2556 }
2557
2558 - (void) addProgressOutput:(NSString *)output {
2559 [self
2560 performSelectorOnMainThread:@selector(_addProgressOutput:)
2561 withObject:output
2562 waitUntilDone:YES
2563 ];
2564 }
2565
2566 - (void) _setConfigurationData:(NSString *)data {
2567 _assert(conffile_r(data));
2568
2569 NSString *ofile = conffile_r[1];
2570 //NSString *nfile = conffile_r[2];
2571
2572 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2573 initWithTitle:@"Configuration Upgrade"
2574 buttons:[NSArray arrayWithObjects:
2575 @"Keep My Old Copy",
2576 @"Accept The New Copy",
2577 // XXX: @"See What Changed",
2578 nil]
2579 defaultButtonIndex:0
2580 delegate:self
2581 context:@"conffile"
2582 ] autorelease];
2583
2584 [sheet setBodyText:[NSString stringWithFormat:
2585 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2586 , ofile]];
2587
2588 [sheet popupAlertAnimated:YES];
2589 }
2590
2591 - (void) _setProgressTitle:(NSString *)title {
2592 [status_ setText:[title stringByAppendingString:@"..."]];
2593 }
2594
2595 - (void) _setProgressPercent:(NSNumber *)percent {
2596 [progress_ setProgress:[percent floatValue]];
2597 }
2598
2599 - (void) _addProgressOutput:(NSString *)output {
2600 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2601 CGSize size = [output_ contentSize];
2602 CGRect rect = {{0, size.height}, {size.width, 0}};
2603 [output_ scrollRectToVisible:rect animated:YES];
2604 }
2605
2606 - (BOOL) isRunning {
2607 return running_;
2608 }
2609
2610 @end
2611 /* }}} */
2612
2613 /* Package Cell {{{ */
2614 @interface PackageCell : UITableCell {
2615 UIImageView *icon_;
2616 UITextLabel *name_;
2617 UITextLabel *description_;
2618 UITextLabel *source_;
2619 //UIImageView *trusted_;
2620 #ifdef USE_BADGES
2621 UIImageView *badge_;
2622 UITextLabel *status_;
2623 #endif
2624 BOOL setup_;
2625 }
2626
2627 - (PackageCell *) init;
2628 - (void) setPackage:(Package *)package;
2629
2630 - (void) _setSelected:(float)fraction;
2631 - (void) setSelected:(BOOL)selected;
2632 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2633 - (void) _setSelectionFadeFraction:(float)fraction;
2634
2635 + (int) heightForPackage:(Package *)package;
2636
2637 @end
2638
2639 @implementation PackageCell
2640
2641 - (void) dealloc {
2642 [icon_ release];
2643 [name_ release];
2644 [description_ release];
2645 [source_ release];
2646 #ifdef USE_BADGES
2647 [badge_ release];
2648 [status_ release];
2649 #endif
2650 //[trusted_ release];
2651 [super dealloc];
2652 }
2653
2654 - (PackageCell *) init {
2655 if ((self = [super init]) != nil) {
2656 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2657 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2658 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
2659
2660 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
2661
2662 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2663 [name_ setBackgroundColor:Clear_];
2664 [name_ setFont:bold];
2665
2666 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2667 [source_ setBackgroundColor:Clear_];
2668 [source_ setFont:large];
2669
2670 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2671 [description_ setBackgroundColor:Clear_];
2672 [description_ setFont:small];
2673
2674 /*trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2675 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];*/
2676
2677 #ifdef USE_BADGES
2678 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2679
2680 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2681 [status_ setBackgroundColor:Clear_];
2682 [status_ setFont:small];
2683 #endif
2684
2685 /*[icon_ setImage:[UIImage applicationImageNamed:@"unknown.png"]];
2686 [icon_ zoomToScale:0.5];
2687 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];*/
2688
2689 [self addSubview:icon_];
2690 [self addSubview:name_];
2691 [self addSubview:description_];
2692 [self addSubview:source_];
2693
2694 CFRelease(small);
2695 CFRelease(large);
2696 CFRelease(bold);
2697 } return self;
2698 }
2699
2700 - (void) setPackage:(Package *)package {
2701 /*if (setup_)
2702 return;
2703 else
2704 setup_ = YES;*/
2705
2706 Source *source = [package source];
2707
2708 UIImage *image = nil;
2709 if (NSString *icon = [package icon])
2710 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2711 if (image == nil) if (NSString *section = [package section])
2712 image = [UIImage applicationImageNamed:[Simplify(section) stringByAppendingString:@".png"]];
2713 /*if (image == nil) if (NSString *icon = [source defaultIcon])
2714 image = [UIImage imageAtPath:[icon substringFromIndex:6]];*/
2715 if (image == nil)
2716 image = [UIImage applicationImageNamed:@"unknown.png"];
2717 [icon_ setImage:image];
2718
2719 /*if (image != nil) {
2720 CGSize size = [image size];
2721 float scale = 30 / std::max(size.width, size.height);
2722 [icon_ zoomToScale:scale];
2723 }*/
2724
2725 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
2726
2727 [name_ setText:[package name]];
2728 [description_ setText:[package tagline]];
2729
2730 NSString *label = nil;
2731 bool trusted = false;
2732
2733 if (source != nil) {
2734 label = [source label];
2735 trusted = [source trusted];
2736 } else if ([[package id] isEqualToString:@"firmware"])
2737 label = @"Apple";
2738
2739 if (label == nil)
2740 label = @"Unknown/Local";
2741
2742 NSString *from = [NSString stringWithFormat:@"from %@", label];
2743
2744 NSString *section = Simplify([package section]);
2745 if (section != nil && ![section isEqualToString:label])
2746 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2747
2748 [source_ setText:from];
2749
2750 #ifdef USE_BADGES
2751 [badge_ removeFromSuperview];
2752 [status_ removeFromSuperview];
2753
2754 if (NSString *mode = [package mode]) {
2755 [badge_ setImage:[UIImage applicationImageNamed:
2756 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2757 ]];
2758
2759 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2760 [status_ setColor:Blueish_];
2761 } else if ([package half]) {
2762 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2763 [status_ setText:@"Package Damaged"];
2764 [status_ setColor:Red_];
2765 } else {
2766 [badge_ setImage:nil];
2767 [status_ setText:nil];
2768 goto done;
2769 }
2770
2771 [self addSubview:badge_];
2772 [self addSubview:status_];
2773 done:;
2774 #endif
2775 }
2776
2777 - (void) _setSelected:(float)fraction {
2778 CGColor black(space_,
2779 Interpolate(0.0, 1.0, fraction),
2780 Interpolate(0.0, 1.0, fraction),
2781 Interpolate(0.0, 1.0, fraction),
2782 1.0);
2783
2784 CGColor gray(space_,
2785 Interpolate(0.4, 1.0, fraction),
2786 Interpolate(0.4, 1.0, fraction),
2787 Interpolate(0.4, 1.0, fraction),
2788 1.0);
2789
2790 [name_ setColor:black];
2791 [description_ setColor:gray];
2792 [source_ setColor:black];
2793 }
2794
2795 - (void) setSelected:(BOOL)selected {
2796 [self _setSelected:(selected ? 1.0 : 0.0)];
2797 [super setSelected:selected];
2798 }
2799
2800 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2801 if (!fade)
2802 [self _setSelected:(selected ? 1.0 : 0.0)];
2803 [super setSelected:selected withFade:fade];
2804 }
2805
2806 - (void) _setSelectionFadeFraction:(float)fraction {
2807 [self _setSelected:fraction];
2808 [super _setSelectionFadeFraction:fraction];
2809 }
2810
2811 + (int) heightForPackage:(Package *)package {
2812 NSString *tagline([package tagline]);
2813 int height = tagline == nil || [tagline length] == 0 ? -15 : 0;
2814 #ifdef USE_BADGES
2815 if ([package hasMode] || [package half])
2816 return height + 96;
2817 else
2818 #endif
2819 return height + 73;
2820 }
2821
2822 @end
2823 /* }}} */
2824 /* Section Cell {{{ */
2825 @interface SectionCell : UITableCell {
2826 NSString *section_;
2827 UITextLabel *name_;
2828 UITextLabel *count_;
2829 UISwitchControl *switch_;
2830 BOOL editing_;
2831 }
2832
2833 - (id) init;
2834 - (void) setSection:(Section *)section editing:(BOOL)editing;
2835
2836 - (void) _setSelected:(float)fraction;
2837 - (void) setSelected:(BOOL)selected;
2838 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2839 - (void) _setSelectionFadeFraction:(float)fraction;
2840
2841 @end
2842
2843 @implementation SectionCell
2844
2845 - (void) dealloc {
2846 if (section_ != nil)
2847 [section_ release];
2848 [name_ release];
2849 [count_ release];
2850 [switch_ release];
2851 [super dealloc];
2852 }
2853
2854 - (id) init {
2855 if ((self = [super init]) != nil) {
2856 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2857 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2858
2859 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
2860 [name_ setBackgroundColor:Clear_];
2861 [name_ setFont:bold];
2862
2863 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2864 [count_ setCentersHorizontally:YES];
2865 [count_ setBackgroundColor:Clear_];
2866 [count_ setFont:small];
2867 [count_ setColor:White_];
2868
2869 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2870 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2871
2872 switch_ = [[UISwitchControl alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
2873 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
2874
2875 [self addSubview:folder];
2876 [self addSubview:name_];
2877 [self addSubview:count_];
2878
2879 [self _setSelected:0];
2880
2881 CFRelease(small);
2882 CFRelease(bold);
2883 } return self;
2884 }
2885
2886 - (void) onSwitch:(id)sender {
2887 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
2888 if (metadata == nil) {
2889 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
2890 [Sections_ setObject:metadata forKey:section_];
2891 }
2892
2893 Changed_ = true;
2894 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
2895 }
2896
2897 - (void) setSection:(Section *)section editing:(BOOL)editing {
2898 if (editing != editing_) {
2899 if (editing_)
2900 [switch_ removeFromSuperview];
2901 else
2902 [self addSubview:switch_];
2903 editing_ = editing;
2904 }
2905
2906 if (section_ != nil) {
2907 [section_ release];
2908 section_ = nil;
2909 }
2910
2911 if (section == nil) {
2912 [name_ setText:@"All Packages"];
2913 [count_ setText:nil];
2914 } else {
2915 section_ = [section name];
2916 if (section_ != nil)
2917 section_ = [section_ retain];
2918 [name_ setText:(section_ == nil ? @"(No Section)" : section_)];
2919 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2920
2921 if (editing_)
2922 [switch_ setValue:isSectionVisible(section_) animated:NO];
2923 }
2924
2925 [name_ setFrame:CGRectMake(48, 9, editing_ ? 165 : 250, 25)];
2926 }
2927
2928 - (void) _setSelected:(float)fraction {
2929 CGColor black(space_,
2930 Interpolate(0.0, 1.0, fraction),
2931 Interpolate(0.0, 1.0, fraction),
2932 Interpolate(0.0, 1.0, fraction),
2933 1.0);
2934
2935 [name_ setColor:black];
2936 }
2937
2938 - (void) setSelected:(BOOL)selected {
2939 [self _setSelected:(selected ? 1.0 : 0.0)];
2940 [super setSelected:selected];
2941 }
2942
2943 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2944 if (!fade)
2945 [self _setSelected:(selected ? 1.0 : 0.0)];
2946 [super setSelected:selected withFade:fade];
2947 }
2948
2949 - (void) _setSelectionFadeFraction:(float)fraction {
2950 [self _setSelected:fraction];
2951 [super _setSelectionFadeFraction:fraction];
2952 }
2953
2954 @end
2955 /* }}} */
2956
2957 /* File Table {{{ */
2958 @interface FileTable : RVPage {
2959 _transient Database *database_;
2960 Package *package_;
2961 NSString *name_;
2962 NSMutableArray *files_;
2963 UITable *list_;
2964 }
2965
2966 - (id) initWithBook:(RVBook *)book database:(Database *)database;
2967 - (void) setPackage:(Package *)package;
2968
2969 @end
2970
2971 @implementation FileTable
2972
2973 - (void) dealloc {
2974 if (package_ != nil)
2975 [package_ release];
2976 if (name_ != nil)
2977 [name_ release];
2978 [files_ release];
2979 [list_ release];
2980 [super dealloc];
2981 }
2982
2983 - (int) numberOfRowsInTable:(UITable *)table {
2984 return files_ == nil ? 0 : [files_ count];
2985 }
2986
2987 - (float) table:(UITable *)table heightForRow:(int)row {
2988 return 24;
2989 }
2990
2991 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2992 if (reusing == nil) {
2993 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
2994 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2995 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
2996 CFRelease(font);
2997 }
2998 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
2999 return reusing;
3000 }
3001
3002 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
3003 return NO;
3004 }
3005
3006 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3007 if ((self = [super initWithBook:book]) != nil) {
3008 database_ = database;
3009
3010 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
3011
3012 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3013 [self addSubview:list_];
3014
3015 UITableColumn *column = [[[UITableColumn alloc]
3016 initWithTitle:@"Name"
3017 identifier:@"name"
3018 width:[self frame].size.width
3019 ] autorelease];
3020
3021 [list_ setDataSource:self];
3022 [list_ setSeparatorStyle:1];
3023 [list_ addTableColumn:column];
3024 [list_ setDelegate:self];
3025 [list_ setReusesTableCells:YES];
3026 } return self;
3027 }
3028
3029 - (void) setPackage:(Package *)package {
3030 if (package_ != nil) {
3031 [package_ autorelease];
3032 package_ = nil;
3033 }
3034
3035 if (name_ != nil) {
3036 [name_ release];
3037 name_ = nil;
3038 }
3039
3040 [files_ removeAllObjects];
3041
3042 if (package != nil) {
3043 package_ = [package retain];
3044 name_ = [[package id] retain];
3045
3046 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
3047
3048 {
3049 std::ifstream fin([path UTF8String]);
3050 std::string line;
3051 while (std::getline(fin, line))
3052 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
3053 }
3054
3055 if ([files_ count] != 0) {
3056 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3057 [files_ removeObjectAtIndex:0];
3058 [files_ sortUsingSelector:@selector(compareByPath:)];
3059
3060 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3061 [stack addObject:@"/"];
3062
3063 for (int i(0), e([files_ count]); i != e; ++i) {
3064 NSString *file = [files_ objectAtIndex:i];
3065 while (![file hasPrefix:[stack lastObject]])
3066 [stack removeLastObject];
3067 NSString *directory = [stack lastObject];
3068 [stack addObject:[file stringByAppendingString:@"/"]];
3069 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
3070 ([stack count] - 2) * 3, "",
3071 [file substringFromIndex:[directory length]]
3072 ]];
3073 }
3074 }
3075 }
3076
3077 [list_ reloadData];
3078 }
3079
3080 - (void) resetViewAnimated:(BOOL)animated {
3081 [list_ resetViewAnimated:animated];
3082 }
3083
3084 - (void) reloadData {
3085 [self setPackage:[database_ packageWithName:name_]];
3086 [self reloadButtons];
3087 }
3088
3089 - (NSString *) title {
3090 return @"Installed Files";
3091 }
3092
3093 - (NSString *) backButtonTitle {
3094 return @"Files";
3095 }
3096
3097 @end
3098 /* }}} */
3099 /* Package View {{{ */
3100 @protocol PackageViewDelegate
3101 - (void) performPackage:(Package *)package;
3102 @end
3103
3104 @interface PackageView : RVPage {
3105 _transient Database *database_;
3106 UIPreferencesTable *table_;
3107 Package *package_;
3108 NSString *name_;
3109 UITextView *description_;
3110 NSMutableArray *buttons_;
3111 }
3112
3113 - (id) initWithBook:(RVBook *)book database:(Database *)database;
3114 - (void) setPackage:(Package *)package;
3115
3116 @end
3117
3118 @implementation PackageView
3119
3120 - (void) dealloc {
3121 [table_ setDataSource:nil];
3122 [table_ setDelegate:nil];
3123
3124 if (package_ != nil)
3125 [package_ release];
3126 if (name_ != nil)
3127 [name_ release];
3128 if (description_ != nil)
3129 [description_ release];
3130 [table_ release];
3131 [buttons_ release];
3132 [super dealloc];
3133 }
3134
3135 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
3136 int number = 2;
3137 if ([package_ installed] != nil)
3138 ++number;
3139 if ([package_ source] != nil)
3140 ++number;
3141 return number;
3142 }
3143
3144 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
3145 if (group-- == 0)
3146 return nil;
3147 else if ([package_ installed] != nil && group-- == 0)
3148 return @"Installed Package";
3149 else if (group-- == 0)
3150 return @"Package Details";
3151 else if ([package_ source] != nil && group-- == 0)
3152 return @"Source Information";
3153 else _assert(false);
3154 }
3155
3156 - (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
3157 if (description_ == nil || group != 0 || row != ([package_ author] == nil ? 1 : 2))
3158 return proposed;
3159 else
3160 return [description_ visibleTextRect].size.height + TextViewOffset_;
3161 }
3162
3163 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
3164 if (group-- == 0) {
3165 int number = 1;
3166 if ([package_ author] != nil)
3167 ++number;
3168 if (description_ != nil)
3169 ++number;
3170 if ([package_ website] != nil)
3171 ++number;
3172 return number;
3173 } else if ([package_ installed] != nil && group-- == 0)
3174 return 2;
3175 else if (group-- == 0) {
3176 int number = 2;
3177 if ([package_ size] != 0)
3178 ++number;
3179 if ([package_ maintainer] != nil)
3180 ++number;
3181 if ([package_ sponsor] != nil)
3182 ++number;
3183 if ([package_ relationships] != nil)
3184 ++number;
3185 if ([[package_ source] trusted])
3186 ++number;
3187 return number;
3188 } else if ([package_ source] != nil && group-- == 0) {
3189 Source *source = [package_ source];
3190 NSString *description = [source description];
3191 int number = 1;
3192 if (description != nil && ![description isEqualToString:[source label]])
3193 ++number;
3194 if ([source origin] != nil)
3195 ++number;
3196 return number;
3197 } else _assert(false);
3198 }
3199
3200 - (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3201 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3202 [cell setShowSelection:NO];
3203
3204 if (group-- == 0) {
3205 if (false) {
3206 } else if (row-- == 0) {
3207 [cell setTitle:[package_ name]];
3208 [cell setValue:[package_ latest]];
3209 } else if ([package_ author] != nil && row-- == 0) {
3210 [cell setTitle:@"Author"];
3211 [cell setValue:[[package_ author] name]];
3212 [cell setShowDisclosure:YES];
3213 [cell setShowSelection:YES];
3214 } else if (description_ != nil && row-- == 0) {
3215 [cell addSubview:description_];
3216 } else if ([package_ website] != nil && row-- == 0) {
3217 [cell setTitle:@"More Information"];
3218 [cell setShowDisclosure:YES];
3219 [cell setShowSelection:YES];
3220 } else _assert(false);
3221 } else if ([package_ installed] != nil && group-- == 0) {
3222 if (false) {
3223 } else if (row-- == 0) {
3224 [cell setTitle:@"Version"];
3225 NSString *installed([package_ installed]);
3226 [cell setValue:(installed == nil ? @"n/a" : installed)];
3227 } else if (row-- == 0) {
3228 [cell setTitle:@"Filesystem Content"];
3229 [cell setShowDisclosure:YES];
3230 [cell setShowSelection:YES];
3231 } else _assert(false);
3232 } else if (group-- == 0) {
3233 if (false) {
3234 } else if (row-- == 0) {
3235 [cell setTitle:@"Identifier"];
3236 [cell setValue:[package_ id]];
3237 } else if (row-- == 0) {
3238 [cell setTitle:@"Section"];
3239 NSString *section([package_ section]);
3240 [cell setValue:(section == nil ? @"n/a" : section)];
3241 } else if ([package_ size] != 0 && row-- == 0) {
3242 [cell setTitle:@"Expanded Size"];
3243 [cell setValue:SizeString([package_ size])];
3244 } else if ([package_ maintainer] != nil && row-- == 0) {
3245 [cell setTitle:@"Maintainer"];
3246 [cell setValue:[[package_ maintainer] name]];
3247 [cell setShowDisclosure:YES];
3248 [cell setShowSelection:YES];
3249 } else if ([package_ sponsor] != nil && row-- == 0) {
3250 [cell setTitle:@"Sponsor"];
3251 [cell setValue:[[package_ sponsor] name]];
3252 [cell setShowDisclosure:YES];
3253 [cell setShowSelection:YES];
3254 } else if ([package_ relationships] != nil && row-- == 0) {
3255 [cell setTitle:@"Package Relationships"];
3256 [cell setShowDisclosure:YES];
3257 [cell setShowSelection:YES];
3258 } else if ([[package_ source] trusted] && row-- == 0) {
3259 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3260 [cell setValue:@"This package has been signed."];
3261 } else _assert(false);
3262 } else if ([package_ source] != nil && group-- == 0) {
3263 Source *source = [package_ source];
3264 NSString *description = [source description];
3265
3266 if (false) {
3267 } else if (row-- == 0) {
3268 NSString *label = [source label];
3269 if (label == nil)
3270 label = [source uri];
3271 [cell setTitle:label];
3272 [cell setValue:[source version]];
3273 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3274 [cell setValue:description];
3275 } else if ([source origin] != nil && row-- == 0) {
3276 [cell setTitle:@"Origin"];
3277 [cell setValue:[source origin]];
3278 } else _assert(false);
3279 } else _assert(false);
3280
3281 return cell;
3282 }
3283
3284 - (BOOL) canSelectRow:(int)row {
3285 return YES;
3286 }
3287
3288 - (void) tableRowSelected:(NSNotification *)notification {
3289 int row = [table_ selectedRow];
3290 if (row == INT_MAX)
3291 return;
3292
3293 #define _else else goto _label; return; } _label:
3294
3295 if (true) {
3296 if (row-- == 0) {
3297 } else if (row-- == 0) {
3298 } else if ([package_ author] != nil && row-- == 0) {
3299 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3300 [[package_ author] email],
3301 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3302 [package_ name]
3303 ] stringByAddingPercentEscapes]
3304 ]]];
3305 } else if (description_ != nil && row-- == 0) {
3306 } else if ([package_ website] != nil && row-- == 0) {
3307 NSURL *url = [NSURL URLWithString:[package_ website]];
3308 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3309 [browser setDelegate:delegate_];
3310 [book_ pushPage:browser];
3311 [browser loadURL:url];
3312 } _else if ([package_ installed] != nil) {
3313 if (row-- == 0) {
3314 } else if (row-- == 0) {
3315 } else if (row-- == 0) {
3316 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3317 [files setDelegate:delegate_];
3318 [files setPackage:package_];
3319 [book_ pushPage:files];
3320 } _else if (true) {
3321 if (row-- == 0) {
3322 } else if (row-- == 0) {
3323 } else if (row-- == 0) {
3324 } else if ([package_ size] != 0 && row-- == 0) {
3325 } else if ([package_ maintainer] != nil && row-- == 0) {
3326 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3327 [[package_ maintainer] email],
3328 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3329 [package_ name]
3330 ] stringByAddingPercentEscapes]
3331 ]]];
3332 } else if ([package_ sponsor] != nil && row-- == 0) {
3333 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3334 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3335 [browser setDelegate:delegate_];
3336 [book_ pushPage:browser];
3337 [browser loadURL:url];
3338 } else if ([package_ relationships] != nil && row-- == 0) {
3339 } else if ([[package_ source] trusted] && row-- == 0) {
3340 } _else if ([package_ source] != nil) {
3341 Source *source = [package_ source];
3342 NSString *description = [source description];
3343
3344 if (row-- == 0) {
3345 } else if (row-- == 0) {
3346 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3347 } else if ([source origin] != nil && row-- == 0) {
3348 } _else _assert(false);
3349
3350 #undef _else
3351 }
3352
3353 - (void) _clickButtonWithName:(NSString *)name {
3354 if ([name isEqualToString:@"Install"])
3355 [delegate_ installPackage:package_];
3356 else if ([name isEqualToString:@"Reinstall"])
3357 [delegate_ installPackage:package_];
3358 else if ([name isEqualToString:@"Remove"])
3359 [delegate_ removePackage:package_];
3360 else if ([name isEqualToString:@"Upgrade"])
3361 [delegate_ installPackage:package_];
3362 else _assert(false);
3363 }
3364
3365 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3366 int count = [buttons_ count];
3367 _assert(count != 0);
3368 _assert(button <= count + 1);
3369
3370 if (count != button - 1)
3371 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3372
3373 [sheet dismiss];
3374 }
3375
3376 - (void) _rightButtonClicked {
3377 int count = [buttons_ count];
3378 _assert(count != 0);
3379
3380 if (count == 1)
3381 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3382 else {
3383 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3384 [buttons addObjectsFromArray:buttons_];
3385 [buttons addObject:@"Cancel"];
3386
3387 [delegate_ slideUp:[[[UIAlertSheet alloc]
3388 initWithTitle:nil
3389 buttons:buttons
3390 defaultButtonIndex:2
3391 delegate:self
3392 context:@"manage"
3393 ] autorelease]];
3394 }
3395 }
3396
3397 - (NSString *) rightButtonTitle {
3398 int count = [buttons_ count];
3399 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
3400 }
3401
3402 - (NSString *) title {
3403 return @"Details";
3404 }
3405
3406 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3407 if ((self = [super initWithBook:book]) != nil) {
3408 database_ = database;
3409
3410 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3411 [self addSubview:table_];
3412
3413 [table_ setDataSource:self];
3414 [table_ setDelegate:self];
3415
3416 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
3417 } return self;
3418 }
3419
3420 - (void) setPackage:(Package *)package {
3421 if (package_ != nil) {
3422 [package_ autorelease];
3423 package_ = nil;
3424 }
3425
3426 if (name_ != nil) {
3427 [name_ release];
3428 name_ = nil;
3429 }
3430
3431 if (description_ != nil) {
3432 [description_ release];
3433 description_ = nil;
3434 }
3435
3436 [buttons_ removeAllObjects];
3437
3438 if (package != nil) {
3439 package_ = [package retain];
3440 name_ = [[package id] retain];
3441
3442 NSString *description([package description]);
3443 if (description == nil)
3444 description = [package tagline];
3445 if (description != nil) {
3446 description_ = [GetTextView(description, 12, true) retain];
3447 [description_ setTextColor:Black_];
3448 }
3449
3450 [table_ reloadData];
3451
3452 if ([package_ source] == nil);
3453 else if ([package_ upgradableAndEssential:NO])
3454 [buttons_ addObject:@"Upgrade"];
3455 else if ([package_ installed] == nil)
3456 [buttons_ addObject:@"Install"];
3457 else
3458 [buttons_ addObject:@"Reinstall"];
3459 if ([package_ installed] != nil)
3460 [buttons_ addObject:@"Remove"];
3461 }
3462 }
3463
3464 - (void) resetViewAnimated:(BOOL)animated {
3465 [table_ resetViewAnimated:animated];
3466 }
3467
3468 - (void) reloadData {
3469 [self setPackage:[database_ packageWithName:name_]];
3470 [self reloadButtons];
3471 }
3472
3473 @end
3474 /* }}} */
3475 /* Package Table {{{ */
3476 @interface PackageTable : RVPage {
3477 _transient Database *database_;
3478 NSString *title_;
3479 SEL filter_;
3480 id object_;
3481 NSMutableArray *packages_;
3482 NSMutableArray *sections_;
3483 UISectionList *list_;
3484 }
3485
3486 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
3487
3488 - (void) setDelegate:(id)delegate;
3489 - (void) setObject:(id)object;
3490
3491 - (void) reloadData;
3492 - (void) resetCursor;
3493
3494 - (UISectionList *) list;
3495
3496 - (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3497
3498 @end
3499
3500 @implementation PackageTable
3501
3502 - (void) dealloc {
3503 [list_ setDataSource:nil];
3504
3505 [title_ release];
3506 if (object_ != nil)
3507 [object_ release];
3508 [packages_ release];
3509 [sections_ release];
3510 [list_ release];
3511 [super dealloc];
3512 }
3513
3514 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
3515 return [sections_ count];
3516 }
3517
3518 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3519 return [[sections_ objectAtIndex:section] name];
3520 }
3521
3522 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3523 return [[sections_ objectAtIndex:section] row];
3524 }
3525
3526 - (int) numberOfRowsInTable:(UITable *)table {
3527 return [packages_ count];
3528 }
3529
3530 - (float) table:(UITable *)table heightForRow:(int)row {
3531 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
3532 }
3533
3534 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3535 if (reusing == nil)
3536 reusing = [[[PackageCell alloc] init] autorelease];
3537 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3538 return reusing;
3539 }
3540
3541 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3542 return NO;
3543 }
3544
3545 - (void) tableRowSelected:(NSNotification *)notification {
3546 int row = [[notification object] selectedRow];
3547 if (row == INT_MAX)
3548 return;
3549
3550 Package *package = [packages_ objectAtIndex:row];
3551 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3552 [view setDelegate:delegate_];
3553 [view setPackage:package];
3554 [book_ pushPage:view];
3555 }
3556
3557 - (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3558 if ((self = [super initWithBook:book]) != nil) {
3559 database_ = database;
3560 title_ = [title retain];
3561 filter_ = filter;
3562 object_ = object == nil ? nil : [object retain];
3563
3564 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3565 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
3566
3567 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3568 [list_ setDataSource:self];
3569
3570 UITableColumn *column = [[[UITableColumn alloc]
3571 initWithTitle:@"Name"
3572 identifier:@"name"
3573 width:[self frame].size.width
3574 ] autorelease];
3575
3576 UITable *table = [list_ table];
3577 [table setSeparatorStyle:1];
3578 [table addTableColumn:column];
3579 [table setDelegate:self];
3580 [table setReusesTableCells:YES];
3581
3582 [self addSubview:list_];
3583 [self reloadData];
3584 } return self;
3585 }
3586
3587 - (void) setDelegate:(id)delegate {
3588 delegate_ = delegate;
3589 }
3590
3591 - (void) setObject:(id)object {
3592 if (object_ != nil)
3593 [object_ release];
3594 if (object == nil)
3595 object_ = nil;
3596 else
3597 object_ = [object retain];
3598 }
3599
3600 - (void) reloadData {
3601 NSArray *packages = [database_ packages];
3602
3603 [packages_ removeAllObjects];
3604 [sections_ removeAllObjects];
3605
3606 for (size_t i(0); i != [packages count]; ++i) {
3607 Package *package([packages objectAtIndex:i]);
3608 if ([[package performSelector:filter_ withObject:object_] boolValue])
3609 [packages_ addObject:package];
3610 }
3611
3612 Section *section = nil;
3613
3614 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3615 Package *package = [packages_ objectAtIndex:offset];
3616 NSString *name = [package index];
3617
3618 if (section == nil || ![[section name] isEqualToString:name]) {
3619 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3620 [sections_ addObject:section];
3621 }
3622
3623 [section addToCount];
3624 }
3625
3626 [list_ reloadData];
3627 }
3628
3629 - (NSString *) title {
3630 return title_;
3631 }
3632
3633 - (void) resetViewAnimated:(BOOL)animated {
3634 [list_ resetViewAnimated:animated];
3635 }
3636
3637 - (void) resetCursor {
3638 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3639 }
3640
3641 - (UISectionList *) list {
3642 return list_;
3643 }
3644
3645 - (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3646 [list_ setShouldHideHeaderInShortLists:hide];
3647 }
3648
3649 @end
3650 /* }}} */
3651
3652 /* Browser Implementation {{{ */
3653 @implementation BrowserView
3654
3655 - (void) dealloc {
3656 WebView *webview = [webview_ webView];
3657 [webview setFrameLoadDelegate:nil];
3658 [webview setResourceLoadDelegate:nil];
3659 [webview setUIDelegate:nil];
3660
3661 [scroller_ setDelegate:nil];
3662 [webview_ setDelegate:nil];
3663
3664 [scroller_ release];
3665 [webview_ release];
3666 [urls_ release];
3667 [indicator_ release];
3668 if (title_ != nil)
3669 [title_ release];
3670 [super dealloc];
3671 }
3672
3673 - (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3674 NSMutableURLRequest *request = [NSMutableURLRequest
3675 requestWithURL:url
3676 cachePolicy:policy
3677 timeoutInterval:30.0
3678 ];
3679
3680 [request addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3681 [request addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
3682 [request addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
3683
3684 [self loadRequest:request];
3685 }
3686
3687
3688 - (void) loadURL:(NSURL *)url {
3689 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3690 }
3691
3692 // XXX: this needs to add the headers
3693 - (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3694 return request;
3695 }
3696
3697 - (void) loadRequest:(NSURLRequest *)request {
3698 pushed_ = true;
3699 [webview_ loadRequest:request];
3700 }
3701
3702 - (void) reloadURL {
3703 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3704 [urls_ removeLastObject];
3705 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3706 }
3707
3708 - (WebView *) webView {
3709 return [webview_ webView];
3710 }
3711
3712 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3713 [scroller_ setContentSize:frame.size];
3714 }
3715
3716 - (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3717 [self view:sender didSetFrame:frame];
3718 }
3719
3720 - (void) getAppTapp:(NSString *)href {
3721 if ([href hasPrefix:@"apptapp://package/"]) {
3722 NSString *name = [href substringFromIndex:18];
3723 Package *package = [database_ packageWithName:name];
3724 if (package == nil) {
3725 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3726 initWithTitle:@"Cannot Locate Package"
3727 buttons:[NSArray arrayWithObjects:@"Close", nil]
3728 defaultButtonIndex:0
3729 delegate:self
3730 context:@"missing"
3731 ] autorelease];
3732
3733 [sheet setBodyText:[NSString stringWithFormat:
3734 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
3735 , name]];
3736
3737 [sheet popupAlertAnimated:YES];
3738 } else {
3739 [self setBackButtonTitle:title_];
3740 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3741 [view setDelegate:delegate_];
3742 [view setPackage:package];
3743 [book_ pushPage:view];
3744 }
3745 }
3746 }
3747
3748 - (void) webView:(WebView *)sender willClickElement:(id)element {
3749 if (![element respondsToSelector:@selector(href)])
3750 return;
3751 NSString *href = [element href];
3752 if (href == nil)
3753 return;
3754 if ([href hasPrefix:@"apptapp://package/"])
3755 [self getAppTapp:href];
3756 }
3757
3758 - (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3759 if ([[[request URL] scheme] isEqualToString:@"apptapp"]) {
3760 [self getAppTapp:[[request URL] absoluteString]];
3761 return nil;
3762 }
3763
3764 if (!pushed_) {
3765 pushed_ = true;
3766 [book_ pushPage:self];
3767 }
3768
3769 return [self _addHeadersToRequest:request];
3770 }
3771
3772 - (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3773 if (request != nil && [[[request URL] scheme] isEqualToString:@"apptapp"])
3774 return nil;
3775 else {
3776 [self setBackButtonTitle:title_];
3777
3778 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3779 [browser setDelegate:delegate_];
3780
3781 if (request != nil) {
3782 [browser loadRequest:[self _addHeadersToRequest:request]];
3783 [book_ pushPage:browser];
3784 }
3785
3786 return [browser webView];
3787 }
3788 }
3789
3790 - (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3791 title_ = [title retain];
3792 [self setTitle:title];
3793 }
3794
3795 - (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3796 if ([frame parentFrame] != nil)
3797 return;
3798
3799 reloading_ = false;
3800 loading_ = true;
3801 [indicator_ startAnimation];
3802 [self reloadButtons];
3803
3804 if (title_ != nil) {
3805 [title_ release];
3806 title_ = nil;
3807 }
3808
3809 [self setTitle:@"Loading..."];
3810
3811 WebView *webview = [webview_ webView];
3812 NSString *href = [webview mainFrameURL];
3813 [urls_ addObject:[NSURL URLWithString:href]];
3814
3815 CGRect webrect = [scroller_ frame];
3816 webrect.size.height = 0;
3817 [webview_ setFrame:webrect];
3818 }
3819
3820 - (void) _finishLoading {
3821 if (!reloading_) {
3822 loading_ = false;
3823 [indicator_ stopAnimation];
3824 [self reloadButtons];
3825 }
3826 }
3827
3828 - (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3829 if ([frame parentFrame] != nil)
3830 return;
3831 [self _finishLoading];
3832 }
3833
3834 - (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3835 if ([frame parentFrame] != nil)
3836 return;
3837 [self _finishLoading];
3838 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
3839 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"home" ofType:@"html"]] absoluteString],
3840 [[error localizedDescription] stringByAddingPercentEscapes]
3841 ]]];
3842 }
3843
3844 - (id) initWithBook:(RVBook *)book database:(Database *)database {
3845 if ((self = [super initWithBook:book]) != nil) {
3846 database_ = database;
3847 loading_ = false;
3848
3849 struct CGRect bounds = [self bounds];
3850
3851 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3852 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3853 [self addSubview:pinstripe];
3854
3855 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3856 [self addSubview:scroller_];
3857
3858 [scroller_ setScrollingEnabled:YES];
3859 [scroller_ setAdjustForContentSizeChange:YES];
3860 [scroller_ setClipsSubviews:YES];
3861 [scroller_ setAllowsRubberBanding:YES];
3862 [scroller_ setScrollDecelerationFactor:0.99];
3863 [scroller_ setDelegate:self];
3864
3865 CGRect webrect = [scroller_ bounds];
3866 webrect.size.height = 0;
3867
3868 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3869 [scroller_ addSubview:webview_];
3870
3871 [webview_ setTilingEnabled:YES];
3872 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3873 [webview_ setAutoresizes:YES];
3874 [webview_ setDelegate:self];
3875 //[webview_ setEnabledGestures:2];
3876
3877 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
3878 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
3879 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
3880
3881 Package *package([database_ packageWithName:@"cydia"]);
3882 NSString *application = package == nil ? @"Cydia" : [NSString
3883 stringWithFormat:@"Cydia/%@",
3884 [package installed]
3885 ];
3886
3887 WebView *webview = [webview_ webView];
3888 [webview setApplicationNameForUserAgent:application];
3889 [webview setFrameLoadDelegate:self];
3890 [webview setResourceLoadDelegate:self];
3891 [webview setUIDelegate:self];
3892
3893 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3894 } return self;
3895 }
3896
3897 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3898 [sheet dismiss];
3899 }
3900
3901 - (void) _leftButtonClicked {
3902 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3903 initWithTitle:@"About Cydia Installer"
3904 buttons:[NSArray arrayWithObjects:@"Close", nil]
3905 defaultButtonIndex:0
3906 delegate:self
3907 context:@"about"
3908 ] autorelease];
3909
3910 [sheet setBodyText:
3911 @"Copyright (C) 2008\n"
3912 "Jay Freeman (saurik)\n"
3913 "saurik@saurik.com\n"
3914 "http://www.saurik.com/\n"
3915 "\n"
3916 "The Okori Group\n"
3917 "http://www.theokorigroup.com/\n"
3918 "\n"
3919 "College of Creative Studies,\n"
3920 "University of California,\n"
3921 "Santa Barbara\n"
3922 "http://www.ccs.ucsb.edu/"
3923 ];
3924
3925 [sheet popupAlertAnimated:YES];
3926 }
3927
3928 - (void) _rightButtonClicked {
3929 reloading_ = true;
3930 [self reloadURL];
3931 }
3932
3933 - (NSString *) leftButtonTitle {
3934 return @"About";
3935 }
3936
3937 - (NSString *) rightButtonTitle {
3938 return loading_ ? @"" : @"Reload";
3939 }
3940
3941 - (NSString *) title {
3942 return nil;
3943 }
3944
3945 - (NSString *) backButtonTitle {
3946 return @"Browser";
3947 }
3948
3949 - (void) setPageActive:(BOOL)active {
3950 if (active)
3951 [book_ addSubview:indicator_];
3952 else
3953 [indicator_ removeFromSuperview];
3954 }
3955
3956 - (void) resetViewAnimated:(BOOL)animated {
3957 }
3958
3959 - (void) setPushed:(bool)pushed {
3960 pushed_ = pushed;
3961 }
3962
3963 @end
3964 /* }}} */
3965
3966 @interface CYBook : RVBook <
3967 ProgressDelegate
3968 > {
3969 _transient Database *database_;
3970 UIView *overlay_;
3971 UIProgressIndicator *indicator_;
3972 UITextLabel *prompt_;
3973 UIProgressBar *progress_;
3974 bool updating_;
3975 }
3976
3977 - (id) initWithFrame:(CGRect)frame database:(Database *)database;
3978 - (void) update;
3979 - (BOOL) updating;
3980
3981 @end
3982
3983 /* Source Cell {{{ */
3984 @interface SourceCell : UITableCell {
3985 UITextLabel *description_;
3986 UIRightTextLabel *label_;
3987 UITextLabel *origin_;
3988 }
3989
3990 - (void) dealloc;
3991
3992 - (SourceCell *) initWithSource:(Source *)source;
3993
3994 - (void) _setSelected:(float)fraction;
3995 - (void) setSelected:(BOOL)selected;
3996 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
3997 - (void) _setSelectionFadeFraction:(float)fraction;
3998
3999 @end
4000
4001 @implementation SourceCell
4002
4003 - (void) dealloc {
4004 [description_ release];
4005 [label_ release];
4006 [origin_ release];
4007 [super dealloc];
4008 }
4009
4010 - (SourceCell *) initWithSource:(Source *)source {
4011 if ((self = [super init]) != nil) {
4012 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
4013 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
4014
4015 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
4016 float clear[] = {0, 0, 0, 0};
4017
4018 NSString *description = [source description];
4019 if (description == nil)
4020 description = [source uri];
4021
4022 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 7, 270, 25)];
4023 [description_ setBackgroundColor:CGColorCreate(space, clear)];
4024 [description_ setFont:bold];
4025 [description_ setText:description];
4026
4027 NSString *label = [source label];
4028 if (label == nil)
4029 label = [source type];
4030
4031 label_ = [[UIRightTextLabel alloc] initWithFrame:CGRectMake(290, 32, 90, 25)];
4032 [label_ setBackgroundColor:CGColorCreate(space, clear)];
4033 [label_ setFont:small];
4034 [label_ setText:label];
4035
4036 NSString *origin = [source origin];
4037 if (origin == nil)
4038 origin = [source distribution];
4039
4040 origin_ = [[UITextLabel alloc] initWithFrame:CGRectMake(13, 35, 315, 20)];
4041 [origin_ setBackgroundColor:CGColorCreate(space, clear)];
4042 [origin_ setFont:small];
4043 [origin_ setText:origin];
4044
4045 [self addSubview:description_];
4046 [self addSubview:label_];
4047 [self addSubview:origin_];
4048
4049 CFRelease(small);
4050 CFRelease(bold);
4051 } return self;
4052 }
4053
4054 - (void) _setSelected:(float)fraction {
4055 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
4056
4057 float black[] = {
4058 Interpolate(0.0, 1.0, fraction),
4059 Interpolate(0.0, 1.0, fraction),
4060 Interpolate(0.0, 1.0, fraction),
4061 1.0};
4062
4063 float blue[] = {
4064 Interpolate(0.2, 1.0, fraction),
4065 Interpolate(0.2, 1.0, fraction),
4066 Interpolate(1.0, 1.0, fraction),
4067 1.0};
4068
4069 float gray[] = {
4070 Interpolate(0.4, 1.0, fraction),
4071 Interpolate(0.4, 1.0, fraction),
4072 Interpolate(0.4, 1.0, fraction),
4073 1.0};
4074
4075 [description_ setColor:CGColorCreate(space, black)];
4076 [label_ setColor:CGColorCreate(space, blue)];
4077 [origin_ setColor:CGColorCreate(space, gray)];
4078 }
4079
4080 - (void) setSelected:(BOOL)selected {
4081 [self _setSelected:(selected ? 1.0 : 0.0)];
4082 [super setSelected:selected];
4083 }
4084
4085 - (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
4086 if (!fade)
4087 [self _setSelected:(selected ? 1.0 : 0.0)];
4088 [super setSelected:selected withFade:fade];
4089 }
4090
4091 - (void) _setSelectionFadeFraction:(float)fraction {
4092 [self _setSelected:fraction];
4093 [super _setSelectionFadeFraction:fraction];
4094 }
4095
4096 @end
4097 /* }}} */
4098 /* Source Table {{{ */
4099 @interface SourceTable : RVPage {
4100 _transient Database *database_;
4101 UISectionList *list_;
4102 NSMutableArray *sources_;
4103 UIAlertSheet *alert_;
4104 }
4105
4106 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4107
4108 @end
4109
4110 @implementation SourceTable
4111
4112 - (void) dealloc {
4113 [list_ setDataSource:nil];
4114
4115 if (sources_ != nil)
4116 [sources_ release];
4117 [list_ release];
4118 [super dealloc];
4119 }
4120
4121 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4122 return 1;
4123 }
4124
4125 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4126 return @"Sources";
4127 }
4128
4129 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4130 return 0;
4131 }
4132
4133 - (int) numberOfRowsInTable:(UITable *)table {
4134 return [sources_ count];
4135 }
4136
4137 - (float) table:(UITable *)table heightForRow:(int)row {
4138 return 64;
4139 }
4140
4141 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4142 return [[[SourceCell alloc] initWithSource:[sources_ objectAtIndex:row]] autorelease];
4143 }
4144
4145 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4146 return NO;
4147 }
4148
4149 - (void) tableRowSelected:(NSNotification*)notification {
4150 UITable *table([list_ table]);
4151 int row([table selectedRow]);
4152 if (row == INT_MAX)
4153 return;
4154
4155 [table selectRow:-1 byExtendingSelection:NO withFade:YES];
4156 }
4157
4158 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4159 if ((self = [super initWithBook:book]) != nil) {
4160 database_ = database;
4161 sources_ = nil;
4162
4163 list_ = [[UISectionList alloc] initWithFrame:[self bounds]];
4164
4165 [self addSubview:list_];
4166
4167 [list_ setDataSource:self];
4168 [list_ setShouldHideHeaderInShortLists:NO];
4169
4170 UITableColumn *column = [[UITableColumn alloc]
4171 initWithTitle:@"Name"
4172 identifier:@"name"
4173 width:[self frame].size.width
4174 ];
4175
4176 UITable *table = [list_ table];
4177 [table setSeparatorStyle:1];
4178 [table addTableColumn:column];
4179 [table setDelegate:self];
4180 } return self;
4181 }
4182
4183 - (void) reloadData {
4184 pkgSourceList list;
4185 _assert(list.ReadMainList());
4186
4187 if (sources_ != nil)
4188 [sources_ release];
4189
4190 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4191 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
4192 [sources_ addObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]];
4193
4194 [list_ reloadData];
4195 }
4196
4197 - (void) resetViewAnimated:(BOOL)animated {
4198 [list_ resetViewAnimated:animated];
4199 }
4200
4201 - (NSString *) leftTitle {
4202 return @"Refresh All";
4203 }
4204
4205 - (NSString *) rightTitle {
4206 return @"Edit";
4207 }
4208
4209 @end
4210 /* }}} */
4211
4212 /* Install View {{{ */
4213 @interface InstallView : RVPage {
4214 _transient Database *database_;
4215 NSMutableArray *sections_;
4216 NSMutableArray *filtered_;
4217 UITransitionView *transition_;
4218 UITable *list_;
4219 UIView *accessory_;
4220 BOOL editing_;
4221 }
4222
4223 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4224 - (void) reloadData;
4225 - (void) resetView;
4226
4227 @end
4228
4229 @implementation InstallView
4230
4231 - (void) dealloc {
4232 [list_ setDataSource:nil];
4233 [list_ setDelegate:nil];
4234
4235 [sections_ release];
4236 [filtered_ release];
4237 [transition_ release];
4238 [list_ release];
4239 [accessory_ release];
4240 [super dealloc];
4241 }
4242
4243 - (int) numberOfRowsInTable:(UITable *)table {
4244 return editing_ ? [sections_ count] : [filtered_ count] + 1;
4245 }
4246
4247 - (float) table:(UITable *)table heightForRow:(int)row {
4248 return 45;
4249 }
4250
4251 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4252 if (reusing == nil)
4253 reusing = [[[SectionCell alloc] init] autorelease];
4254 [(SectionCell *)reusing setSection:(editing_ ?
4255 [sections_ objectAtIndex:row] :
4256 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
4257 ) editing:editing_];
4258 return reusing;
4259 }
4260
4261 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4262 return !editing_;
4263 }
4264
4265 - (BOOL) table:(UITable *)table canSelectRow:(int)row {
4266 return !editing_;
4267 }
4268
4269 - (void) tableRowSelected:(NSNotification *)notification {
4270 int row = [[notification object] selectedRow];
4271 if (row == INT_MAX)
4272 return;
4273
4274 Section *section;
4275 NSString *name;
4276 NSString *title;
4277
4278 if (row == 0) {
4279 section = nil;
4280 name = nil;
4281 title = @"All Packages";
4282 } else {
4283 section = [filtered_ objectAtIndex:(row - 1)];
4284 name = [section name];
4285
4286 if (name != nil)
4287 title = name;
4288 else {
4289 name = @"";
4290 title = @"(No Section)";
4291 }
4292 }
4293
4294 PackageTable *table = [[[PackageTable alloc]
4295 initWithBook:book_
4296 database:database_
4297 title:title
4298 filter:@selector(isUninstalledInSection:)
4299 with:name
4300 ] autorelease];
4301
4302 [table setDelegate:delegate_];
4303
4304 [book_ pushPage:table];
4305 }
4306
4307 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4308 if ((self = [super initWithBook:book]) != nil) {
4309 database_ = database;
4310
4311 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4312 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
4313
4314 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
4315 [self addSubview:transition_];
4316
4317 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
4318 [transition_ transition:0 toView:list_];
4319
4320 UITableColumn *column = [[[UITableColumn alloc]
4321 initWithTitle:@"Name"
4322 identifier:@"name"
4323 width:[self frame].size.width
4324 ] autorelease];
4325
4326 [list_ setDataSource:self];
4327 [list_ setSeparatorStyle:1];
4328 [list_ addTableColumn:column];
4329 [list_ setDelegate:self];
4330 [list_ setReusesTableCells:YES];
4331
4332 [self reloadData];
4333 } return self;
4334 }
4335
4336 - (void) reloadData {
4337 NSArray *packages = [database_ packages];
4338
4339 [sections_ removeAllObjects];
4340 [filtered_ removeAllObjects];
4341
4342 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
4343 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
4344
4345 for (size_t i(0); i != [packages count]; ++i) {
4346 Package *package([packages objectAtIndex:i]);
4347 NSString *name([package section]);
4348
4349 if (name != nil) {
4350 Section *section([sections objectForKey:name]);
4351 if (section == nil) {
4352 section = [[[Section alloc] initWithName:name] autorelease];
4353 [sections setObject:section forKey:name];
4354 }
4355 }
4356
4357 if ([package valid] && [package installed] == nil && [package visible])
4358 [filtered addObject:package];
4359 }
4360
4361 [sections_ addObjectsFromArray:[sections allValues]];
4362 [sections_ sortUsingSelector:@selector(compareByName:)];
4363
4364 [filtered sortUsingSelector:@selector(compareBySection:)];
4365
4366 Section *section = nil;
4367 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
4368 Package *package = [filtered objectAtIndex:offset];
4369 NSString *name = [package section];
4370
4371 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
4372 section = name == nil ?
4373 [[[Section alloc] initWithName:nil] autorelease] :
4374 [sections objectForKey:name];
4375 [filtered_ addObject:section];
4376 }
4377
4378 [section addToCount];
4379 }
4380
4381 [list_ reloadData];
4382 }
4383
4384 - (void) resetView {
4385 if (editing_)
4386 [self _rightButtonClicked];
4387 }
4388
4389 - (void) resetViewAnimated:(BOOL)animated {
4390 [list_ resetViewAnimated:animated];
4391 }
4392
4393 - (void) _rightButtonClicked {
4394 if ((editing_ = !editing_))
4395 [list_ reloadData];
4396 else {
4397 [delegate_ updateData];
4398 }
4399
4400 [book_ setTitle:[self title] forPage:self];
4401 [book_ reloadButtonsForPage:self];
4402 }
4403
4404 - (NSString *) title {
4405 return editing_ ? @"Section Visibility" : @"Install by Section";
4406 }
4407
4408 - (NSString *) backButtonTitle {
4409 return @"Sections";
4410 }
4411
4412 - (NSString *) rightButtonTitle {
4413 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
4414 }
4415
4416 - (UIView *) accessoryView {
4417 return accessory_;
4418 }
4419
4420 @end
4421 /* }}} */
4422 /* Changes View {{{ */
4423 @interface ChangesView : RVPage {
4424 _transient Database *database_;
4425 NSMutableArray *packages_;
4426 NSMutableArray *sections_;
4427 UISectionList *list_;
4428 unsigned upgrades_;
4429 }
4430
4431 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4432 - (void) reloadData;
4433
4434 @end
4435
4436 @implementation ChangesView
4437
4438 - (void) dealloc {
4439 [[list_ table] setDelegate:nil];
4440 [list_ setDataSource:nil];
4441
4442 [packages_ release];
4443 [sections_ release];
4444 [list_ release];
4445 [super dealloc];
4446 }
4447
4448 - (int) numberOfSectionsInSectionList:(UISectionList *)list {
4449 return [sections_ count];
4450 }
4451
4452 - (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4453 return [[sections_ objectAtIndex:section] name];
4454 }
4455
4456 - (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4457 return [[sections_ objectAtIndex:section] row];
4458 }
4459
4460 - (int) numberOfRowsInTable:(UITable *)table {
4461 return [packages_ count];
4462 }
4463
4464 - (float) table:(UITable *)table heightForRow:(int)row {
4465 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
4466 }
4467
4468 - (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4469 if (reusing == nil)
4470 reusing = [[[PackageCell alloc] init] autorelease];
4471 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4472 return reusing;
4473 }
4474
4475 - (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4476 return NO;
4477 }
4478
4479 - (void) tableRowSelected:(NSNotification *)notification {
4480 int row = [[notification object] selectedRow];
4481 if (row == INT_MAX)
4482 return;
4483 Package *package = [packages_ objectAtIndex:row];
4484 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4485 [view setDelegate:delegate_];
4486 [view setPackage:package];
4487 [book_ pushPage:view];
4488 }
4489
4490 - (void) _leftButtonClicked {
4491 [(CYBook *)book_ update];
4492 [self reloadButtons];
4493 }
4494
4495 - (void) _rightButtonClicked {
4496 [delegate_ distUpgrade];
4497 }
4498
4499 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4500 if ((self = [super initWithBook:book]) != nil) {
4501 database_ = database;
4502
4503 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4504 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
4505
4506 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4507 [self addSubview:list_];
4508
4509 [list_ setShouldHideHeaderInShortLists:NO];
4510 [list_ setDataSource:self];
4511 //[list_ setSectionListStyle:1];
4512
4513 UITableColumn *column = [[[UITableColumn alloc]
4514 initWithTitle:@"Name"
4515 identifier:@"name"
4516 width:[self frame].size.width
4517 ] autorelease];
4518
4519 UITable *table = [list_ table];
4520 [table setSeparatorStyle:1];
4521 [table addTableColumn:column];
4522 [table setDelegate:self];
4523 [table setReusesTableCells:YES];
4524
4525 [self reloadData];
4526 } return self;
4527 }
4528
4529 - (void) reloadData {
4530 NSArray *packages = [database_ packages];
4531
4532 [packages_ removeAllObjects];
4533 [sections_ removeAllObjects];
4534
4535 for (size_t i(0); i != [packages count]; ++i) {
4536 Package *package([packages objectAtIndex:i]);
4537
4538 if (
4539 [package installed] == nil && [package valid] && [package visible] ||
4540 [package upgradableAndEssential:NO]
4541 )
4542 [packages_ addObject:package];
4543 }
4544
4545 [packages_ sortUsingSelector:@selector(compareForChanges:)];
4546
4547 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
4548 Section *section = nil;
4549
4550 upgrades_ = 0;
4551 bool unseens = false;
4552
4553 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
4554
4555 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4556 Package *package = [packages_ objectAtIndex:offset];
4557
4558 if ([package upgradableAndEssential:YES]) {
4559 ++upgrades_;
4560 [upgradable addToCount];
4561 } else {
4562 unseens = true;
4563 NSDate *seen = [package seen];
4564
4565 NSString *name;
4566
4567 if (seen == nil)
4568 name = [@"n/a ?" retain];
4569 else {
4570 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
4571 }
4572
4573 if (section == nil || ![[section name] isEqualToString:name]) {
4574 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4575 [sections_ addObject:section];
4576 }
4577
4578 [name release];
4579 [section addToCount];
4580 }
4581 }
4582
4583 CFRelease(formatter);
4584
4585 if (unseens) {
4586 Section *last = [sections_ lastObject];
4587 size_t count = [last count];
4588 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4589 [sections_ removeLastObject];
4590 }
4591
4592 if (upgrades_ != 0)
4593 [sections_ insertObject:upgradable atIndex:0];
4594
4595 [list_ reloadData];
4596 [self reloadButtons];
4597 }
4598
4599 - (void) resetViewAnimated:(BOOL)animated {
4600 [list_ resetViewAnimated:animated];
4601 }
4602
4603 - (NSString *) leftButtonTitle {
4604 return [(CYBook *)book_ updating] ? nil : @"Refresh";
4605 }
4606
4607 - (NSString *) rightButtonTitle {
4608 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
4609 }
4610
4611 - (NSString *) title {
4612 return @"Changes";
4613 }
4614
4615 @end
4616 /* }}} */
4617 /* Manage View {{{ */
4618 @interface ManageView : RVPage {
4619 _transient Database *database_;
4620 PackageTable *packages_;
4621 SourceTable *sources_;
4622 }
4623
4624 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4625
4626 @end
4627
4628 @implementation ManageView
4629
4630 - (void) dealloc {
4631 [packages_ release];
4632 [sources_ release];
4633 [super dealloc];
4634 }
4635
4636 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4637 if ((self = [super initWithBook:book]) != nil) {
4638 database_ = database;
4639
4640 packages_ = [[PackageTable alloc]
4641 initWithBook:book
4642 database:database
4643 title:nil
4644 filter:@selector(isInstalledInSection:)
4645 with:nil
4646 ];
4647
4648 sources_ = [[SourceTable alloc]
4649 initWithBook:book
4650 database:database
4651 ];
4652
4653 [self addSubview:packages_];
4654 } return self;
4655 }
4656
4657 - (void) resetViewAnimated:(BOOL)animated {
4658 [packages_ resetViewAnimated:animated];
4659 [sources_ resetViewAnimated:animated];
4660 }
4661
4662 - (void) reloadData {
4663 [packages_ reloadData];
4664 [sources_ reloadData];
4665 }
4666
4667 - (NSString *) title {
4668 return @"Installed Packages";
4669 }
4670
4671 - (NSString *) backButtonTitle {
4672 return @"Packages";
4673 }
4674
4675 - (void) setDelegate:(id)delegate {
4676 [super setDelegate:delegate];
4677 [packages_ setDelegate:delegate];
4678 [sources_ setDelegate:delegate];
4679 }
4680
4681 @end
4682 /* }}} */
4683 /* Search View {{{ */
4684 @protocol SearchViewDelegate
4685 - (void) showKeyboard:(BOOL)show;
4686 @end
4687
4688 @interface SearchView : RVPage {
4689 UIView *accessory_;
4690 UISearchField *field_;
4691 UITransitionView *transition_;
4692 PackageTable *table_;
4693 UIPreferencesTable *advanced_;
4694 UIView *dimmed_;
4695 bool flipped_;
4696 bool reload_;
4697 }
4698
4699 - (id) initWithBook:(RVBook *)book database:(Database *)database;
4700 - (void) reloadData;
4701
4702 @end
4703
4704 @implementation SearchView
4705
4706 - (void) dealloc {
4707 #ifndef __OBJC2__
4708 [[field_ textTraits] setEditingDelegate:nil];
4709 #endif
4710 [field_ setDelegate:nil];
4711
4712 [accessory_ release];
4713 [field_ release];
4714 [transition_ release];
4715 [table_ release];
4716 [advanced_ release];
4717 [dimmed_ release];
4718 [super dealloc];
4719 }
4720
4721 - (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4722 return 1;
4723 }
4724
4725 - (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4726 switch (group) {
4727 case 0: return @"Advanced Search (Coming Soon!)";
4728
4729 default: _assert(false);
4730 }
4731 }
4732
4733 - (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4734 switch (group) {
4735 case 0: return 0;
4736
4737 default: _assert(false);
4738 }
4739 }
4740
4741 - (void) _showKeyboard:(BOOL)show {
4742 CGSize keysize = [UIKeyboard defaultSize];
4743 CGRect keydown = [book_ pageBounds];
4744 CGRect keyup = keydown;
4745 keyup.size.height -= keysize.height - ButtonBarHeight_;
4746
4747 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4748
4749 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4750 [animation setSignificantRectFields:8];
4751
4752 if (show) {
4753 [animation setStartFrame:keydown];
4754 [animation setEndFrame:keyup];
4755 } else {
4756 [animation setStartFrame:keyup];
4757 [animation setEndFrame:keydown];
4758 }
4759
4760 UIAnimator *animator = [UIAnimator sharedAnimator];
4761
4762 [animator
4763 addAnimations:[NSArray arrayWithObjects:animation, nil]
4764 withDuration:(KeyboardTime_ - delay)
4765 start:!show
4766 ];
4767
4768 if (show)
4769 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4770
4771 #ifndef __OBJC2__
4772 [delegate_ showKeyboard:show];
4773 #endif
4774 }
4775
4776 - (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
4777 [self _showKeyboard:YES];
4778 }
4779
4780 - (void) textFieldDidResignFirstResponder:(UITextField *)field {
4781 [self _showKeyboard:NO];
4782 }
4783
4784 - (void) keyboardInputChanged:(UIFieldEditor *)editor {
4785 if (reload_) {
4786 NSString *text([field_ text]);
4787 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
4788 [self reloadData];
4789 reload_ = false;
4790 }
4791 }
4792
4793 - (void) textFieldClearButtonPressed:(UITextField *)field {
4794 reload_ = true;
4795 }
4796
4797 - (void) keyboardInputShouldDelete:(id)input {
4798 reload_ = true;
4799 }
4800
4801 - (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
4802 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
4803 reload_ = true;
4804 return YES;
4805 } else {
4806 [field_ resignFirstResponder];
4807 return NO;
4808 }
4809 }
4810
4811 - (id) initWithBook:(RVBook *)book database:(Database *)database {
4812 if ((self = [super initWithBook:book]) != nil) {
4813 CGRect pageBounds = [book_ pageBounds];
4814
4815 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
4816 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
4817 [self addSubview:pinstripe];*/
4818
4819 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
4820 [self addSubview:transition_];
4821
4822 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
4823
4824 [advanced_ setReusesTableCells:YES];
4825 [advanced_ setDataSource:self];
4826 [advanced_ reloadData];
4827
4828 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
4829 CGColor dimmed(space_, 0, 0, 0, 0.5);
4830 [dimmed_ setBackgroundColor:dimmed];
4831
4832 table_ = [[PackageTable alloc]
4833 initWithBook:book
4834 database:database
4835 title:nil
4836 filter:@selector(isSearchedForBy:)
4837 with:nil
4838 ];
4839
4840 [table_ setShouldHideHeaderInShortLists:NO];
4841 [transition_ transition:0 toView:table_];
4842
4843 CGRect cnfrect = {{
4844 #ifdef __OBJC2__
4845 6 +
4846 #endif
4847 1, 38}, {17, 18}};
4848
4849 CGRect area;
4850 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
4851 area.origin.y = 30;
4852
4853 area.size.width =
4854 #ifdef __OBJC2__
4855 8 +
4856 #endif
4857 [self bounds].size.width - area.origin.x - 18;
4858
4859 area.size.height = [UISearchField defaultHeight];
4860
4861 field_ = [[UISearchField alloc] initWithFrame:area];
4862
4863 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
4864 [field_ setFont:font];
4865 CFRelease(font);
4866
4867 [field_ setPlaceholder:@"Package Names & Descriptions"];
4868 [field_ setDelegate:self];
4869
4870 #ifdef __OBJC2__
4871 [field_ setPaddingTop:3];
4872 #else
4873 [field_ setPaddingTop:5];
4874 #endif
4875
4876 UITextTraits *traits = [field_ textTraits];
4877 [traits setAutoCapsType:0];
4878 [traits setAutoCorrectionType:1];
4879 [traits setReturnKeyType:6];
4880
4881 #ifndef __OBJC2__
4882 [traits setEditingDelegate:self];
4883 #endif
4884
4885 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
4886
4887 accessory_ = [[UIView alloc] initWithFrame:accrect];
4888 [accessory_ addSubview:field_];
4889
4890 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
4891 [configure setShowPressFeedback:YES];
4892 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
4893 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
4894 [accessory_ addSubview:configure];*/
4895 } return self;
4896 }
4897
4898 - (void) flipPage {
4899 #ifndef __OBJC2__
4900 LKAnimation *animation = [LKTransition animation];
4901 [animation setType:@"oglFlip"];
4902 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
4903 [animation setFillMode:@"extended"];
4904 [animation setTransitionFlags:3];
4905 [animation setDuration:10];
4906 [animation setSpeed:0.35];
4907 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
4908 [[transition_ _layer] addAnimation:animation forKey:0];
4909 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
4910 flipped_ = !flipped_;
4911 #endif
4912 }
4913
4914 - (void) configurePushed {
4915 [field_ resignFirstResponder];
4916 [self flipPage];
4917 }
4918
4919 - (void) resetViewAnimated:(BOOL)animated {
4920 if (flipped_)
4921 [self flipPage];
4922 [table_ resetViewAnimated:animated];
4923 }
4924
4925 - (void) reloadData {
4926 if (flipped_)
4927 [self flipPage];
4928 [table_ setObject:[field_ text]];
4929 [table_ reloadData];
4930 [table_ resetCursor];
4931 }
4932
4933 - (UIView *) accessoryView {
4934 return accessory_;
4935 }
4936
4937 - (NSString *) title {
4938 return nil;
4939 }
4940
4941 - (NSString *) backButtonTitle {
4942 return @"Search";
4943 }
4944
4945 - (void) setDelegate:(id)delegate {
4946 [table_ setDelegate:delegate];
4947 [super setDelegate:delegate];
4948 }
4949
4950 @end
4951 /* }}} */
4952
4953 @implementation CYBook
4954
4955 - (void) dealloc {
4956 [overlay_ release];
4957 [indicator_ release];
4958 [prompt_ release];
4959 [progress_ release];
4960 [super dealloc];
4961 }
4962
4963 - (NSString *) getTitleForPage:(RVPage *)page {
4964 return Simplify([super getTitleForPage:page]);
4965 }
4966
4967 - (BOOL) updating {
4968 return updating_;
4969 }
4970
4971 - (void) update {
4972 [navbar_ setPrompt:@""];
4973 [navbar_ addSubview:overlay_];
4974 [indicator_ startAnimation];
4975 [prompt_ setText:@"Updating Database..."];
4976 [progress_ setProgress:0];
4977
4978 updating_ = true;
4979
4980 [NSThread
4981 detachNewThreadSelector:@selector(_update)
4982 toTarget:self
4983 withObject:nil
4984 ];
4985 }
4986
4987 - (void) _update_ {
4988 updating_ = false;
4989
4990 [overlay_ removeFromSuperview];
4991 [indicator_ stopAnimation];
4992 [delegate_ reloadData];
4993
4994 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
4995 }
4996
4997 - (id) initWithFrame:(CGRect)frame database:(Database *)database {
4998 if ((self = [super initWithFrame:frame]) != nil) {
4999 database_ = database;
5000
5001 if (Advanced_)
5002 [navbar_ setBarStyle:1];
5003
5004 CGRect ovrrect = [navbar_ bounds];
5005 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
5006
5007 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
5008
5009 UIProgressIndicatorStyle style = Advanced_ ?
5010 kUIProgressIndicatorStyleSmallWhite :
5011 kUIProgressIndicatorStyleSmallBlack;
5012
5013 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5014 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5015 CGRect indrect = {{indoffset, indoffset}, indsize};
5016
5017 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5018 [indicator_ setStyle:style];
5019 [overlay_ addSubview:indicator_];
5020
5021 CGSize prmsize = {200, indsize.width + 4};
5022
5023 CGRect prmrect = {{
5024 indoffset * 2 + indsize.width,
5025 #ifdef __OBJC2__
5026 -1 +
5027 #endif
5028 (ovrrect.size.height - prmsize.height) / 2
5029 }, prmsize};
5030
5031 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
5032
5033 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5034
5035 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
5036 [prompt_ setBackgroundColor:Clear_];
5037 [prompt_ setFont:font];
5038
5039 CFRelease(font);
5040
5041 [overlay_ addSubview:prompt_];
5042
5043 CGSize prgsize = {75, 100};
5044
5045 CGRect prgrect = {{
5046 ovrrect.size.width - prgsize.width - 10,
5047 (ovrrect.size.height - prgsize.height) / 2
5048 } , prgsize};
5049
5050 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5051 [progress_ setStyle:0];
5052 [overlay_ addSubview:progress_];
5053 } return self;
5054 }
5055
5056 - (void) _update {
5057 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5058
5059 Status status;
5060 status.setDelegate(self);
5061
5062 [database_ updateWithStatus:status];
5063
5064 [self
5065 performSelectorOnMainThread:@selector(_update_)
5066 withObject:nil
5067 waitUntilDone:NO
5068 ];
5069
5070 [pool release];
5071 }
5072
5073 - (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5074 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5075 }
5076
5077 - (void) setProgressTitle:(NSString *)title {
5078 [self
5079 performSelectorOnMainThread:@selector(_setProgressTitle:)
5080 withObject:title
5081 waitUntilDone:YES
5082 ];
5083 }
5084
5085 - (void) setProgressPercent:(float)percent {
5086 }
5087
5088 - (void) addProgressOutput:(NSString *)output {
5089 [self
5090 performSelectorOnMainThread:@selector(_addProgressOutput:)
5091 withObject:output
5092 waitUntilDone:YES
5093 ];
5094 }
5095
5096 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5097 [sheet dismiss];
5098 }
5099
5100 - (void) _setProgressTitle:(NSString *)title {
5101 [prompt_ setText:[title stringByAppendingString:@"..."]];
5102 }
5103
5104 - (void) _addProgressOutput:(NSString *)output {
5105 }
5106
5107 @end
5108
5109 @interface Cydia : UIApplication <
5110 ConfirmationViewDelegate,
5111 ProgressViewDelegate,
5112 SearchViewDelegate,
5113 CydiaDelegate
5114 > {
5115 UIWindow *window_;
5116
5117 UIView *underlay_;
5118 UIView *overlay_;
5119 CYBook *book_;
5120 UIButtonBar *buttonbar_;
5121
5122 ConfirmationView *confirm_;
5123
5124 NSMutableArray *essential_;
5125 NSMutableArray *broken_;
5126
5127 Database *database_;
5128 ProgressView *progress_;
5129
5130 unsigned tag_;
5131
5132 UIKeyboard *keyboard_;
5133 UIProgressHUD *hud_;
5134
5135 InstallView *install_;
5136 ChangesView *changes_;
5137 ManageView *manage_;
5138 SearchView *search_;
5139 }
5140
5141 @end
5142
5143 @implementation Cydia
5144
5145 - (void) _loaded {
5146 if ([broken_ count] != 0) {
5147 int count = [broken_ count];
5148
5149 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5150 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
5151 buttons:[NSArray arrayWithObjects:
5152 @"Forcibly Clear",
5153 @"Ignore (Temporary)",
5154 nil]
5155 defaultButtonIndex:0
5156 delegate:self
5157 context:@"fixhalf"
5158 ] autorelease];
5159
5160 [sheet setBodyText:@"When the shell scripts associated with packages fail, they are left in a bad state known as either half-configured or half-installed. These errors don't go away and instead continue to cause issues. These scripts can be deleted and the packages forcibly removed."];
5161 [sheet popupAlertAnimated:YES];
5162 } else if (!Ignored_ && [essential_ count] != 0) {
5163 int count = [essential_ count];
5164
5165 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5166 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
5167 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
5168 defaultButtonIndex:0
5169 delegate:self
5170 context:@"upgrade"
5171 ] autorelease];
5172
5173 [sheet setBodyText:@"One or more essential packages are currently out of date. If these upgrades are not performed you are likely to encounter errors."];
5174 [sheet popupAlertAnimated:YES];
5175 }
5176 }
5177
5178 - (void) _reloadData {
5179 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
5180 [hud setText:@"Reloading Data"];
5181 [overlay_ addSubview:hud];
5182 [hud show:YES];*/
5183
5184 [database_ reloadData];
5185
5186 if (Packages_ == nil) {
5187 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:128];
5188 [Metadata_ setObject:Packages_ forKey:@"Packages"];
5189 }
5190
5191 if (Sections_ == nil) {
5192 Sections_ = [[NSMutableDictionary alloc] initWithCapacity:32];
5193 [Metadata_ setObject:Sections_ forKey:@"Sections"];
5194 }
5195
5196 size_t changes(0);
5197
5198 [essential_ removeAllObjects];
5199 [broken_ removeAllObjects];
5200
5201 NSArray *packages = [database_ packages];
5202 for (int i(0), e([packages count]); i != e; ++i) {
5203 Package *package = [packages objectAtIndex:i];
5204 if ([package half])
5205 [broken_ addObject:package];
5206 if ([package upgradableAndEssential:NO]) {
5207 if ([package essential])
5208 [essential_ addObject:package];
5209 ++changes;
5210 }
5211 }
5212
5213 if (changes != 0) {
5214 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
5215 [buttonbar_ setBadgeValue:badge forButton:3];
5216 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5217 [buttonbar_ setBadgeAnimated:YES forButton:3];
5218 [self setApplicationBadge:badge];
5219 } else {
5220 [buttonbar_ setBadgeValue:nil forButton:3];
5221 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
5222 [buttonbar_ setBadgeAnimated:NO forButton:3];
5223 [self removeApplicationBadge];
5224 }
5225
5226 [self updateData];
5227
5228 if ([packages count] == 0);
5229 else if (Loaded_)
5230 [self _loaded];
5231 else {
5232 Loaded_ = YES;
5233 [book_ update];
5234 }
5235
5236 /*[hud show:NO];
5237 [hud removeFromSuperview];*/
5238 }
5239
5240 - (void) updateData {
5241 if (Changed_) {
5242 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
5243 Changed_ = false;
5244 }
5245
5246 /* XXX: this is just stupid */
5247 if (tag_ != 2)
5248 [install_ reloadData];
5249 if (tag_ != 3)
5250 [changes_ reloadData];
5251 if (tag_ != 4)
5252 [manage_ reloadData];
5253 if (tag_ != 5)
5254 [search_ reloadData];
5255
5256 [book_ reloadData];
5257 }
5258
5259 - (void) reloadData {
5260 @synchronized (self) {
5261 if (confirm_ == nil)
5262 [self _reloadData];
5263 }
5264 }
5265
5266 - (void) resolve {
5267 pkgProblemResolver *resolver = [database_ resolver];
5268
5269 resolver->InstallProtect();
5270 if (!resolver->Resolve(true))
5271 _error->Discard();
5272 }
5273
5274 - (void) perform {
5275 [database_ prepare];
5276
5277 if ([database_ cache]->BrokenCount() == 0)
5278 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
5279 else {
5280 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
5281 NSArray *packages = [database_ packages];
5282
5283 for (size_t i(0); i != [packages count]; ++i) {
5284 Package *package = [packages objectAtIndex:i];
5285 if ([package broken])
5286 [broken addObject:[package name]];
5287 }
5288
5289 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
5290 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
5291 buttons:[NSArray arrayWithObjects:@"Okay", nil]
5292 defaultButtonIndex:0
5293 delegate:self
5294 context:@"broken"
5295 ] autorelease];
5296
5297 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
5298 [sheet popupAlertAnimated:YES];
5299
5300 [self _reloadData];
5301 }
5302 }
5303
5304 - (void) installPackage:(Package *)package {
5305 @synchronized (self) {
5306 [package install];
5307 [self resolve];
5308 [self perform];
5309 }
5310 }
5311
5312 - (void) removePackage:(Package *)package {
5313 @synchronized (self) {
5314 [package remove];
5315 [self resolve];
5316 [self perform];
5317 }
5318 }
5319
5320 - (void) distUpgrade {
5321 @synchronized (self) {
5322 [database_ upgrade];
5323 [self perform];
5324 }
5325 }
5326
5327 - (void) cancel {
5328 @synchronized (self) {
5329 [confirm_ release];
5330 confirm_ = nil;
5331 [self _reloadData];
5332 }
5333 }
5334
5335 - (void) confirm {
5336 [overlay_ removeFromSuperview];
5337 reload_ = true;
5338
5339 [progress_
5340 detachNewThreadSelector:@selector(perform)
5341 toTarget:database_
5342 withObject:nil
5343 title:@"Running..."
5344 ];
5345 }
5346
5347 - (void) bootstrap_ {
5348 [database_ update];
5349 [database_ upgrade];
5350 [database_ prepare];
5351 [database_ perform];
5352 }
5353
5354 - (void) bootstrap {
5355 [progress_
5356 detachNewThreadSelector:@selector(bootstrap_)
5357 toTarget:self
5358 withObject:nil
5359 title:@"Bootstrap Install..."
5360 ];
5361 }
5362
5363 - (void) progressViewIsComplete:(ProgressView *)progress {
5364 @synchronized (self) {
5365 [self _reloadData];
5366
5367 if (confirm_ != nil) {
5368 [underlay_ addSubview:overlay_];
5369 [confirm_ removeFromSuperview];
5370 [confirm_ release];
5371 confirm_ = nil;
5372 }
5373 }
5374 }
5375
5376 - (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5377 NSString *context = [sheet context];
5378 if ([context isEqualToString:@"fixhalf"])
5379 switch (button) {
5380 case 1:
5381 @synchronized (self) {
5382 for (int i = 0, e = [broken_ count]; i != e; ++i) {
5383 Package *broken = [broken_ objectAtIndex:i];
5384 [broken remove];
5385
5386 NSString *id = [broken id];
5387 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
5388 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
5389 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
5390 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
5391 }
5392
5393 [self resolve];
5394 [self perform];
5395 }
5396 break;
5397
5398 case 2:
5399 [broken_ removeAllObjects];
5400 [self _loaded];
5401 break;
5402
5403 default:
5404 _assert(false);
5405 }
5406 else if ([context isEqualToString:@"upgrade"])
5407 switch (button) {
5408 case 1:
5409 @synchronized (self) {
5410 for (int i = 0, e = [essential_ count]; i != e; ++i) {
5411 Package *essential = [essential_ objectAtIndex:i];
5412 [essential install];
5413 }
5414
5415 [self resolve];
5416 [self perform];
5417 }
5418 break;
5419
5420 case 2:
5421 Ignored_ = YES;
5422 break;
5423
5424 default:
5425 _assert(false);
5426 }
5427
5428 [sheet dismiss];
5429 }
5430
5431 - (void) setPage:(RVPage *)page {
5432 [page resetViewAnimated:NO];
5433 [page setDelegate:self];
5434 [book_ setPage:page];
5435 }
5436
5437 - (RVPage *) _setHomePage {
5438 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
5439 [self setPage:browser];
5440 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
5441 return browser;
5442 }
5443
5444 - (void) buttonBarItemTapped:(id)sender {
5445 unsigned tag = [sender tag];
5446 if (tag == tag_) {
5447 [book_ resetViewAnimated:YES];
5448 return;
5449 } else if (tag_ == 2 && tag != 2)
5450 [install_ resetView];
5451
5452 switch (tag) {
5453 case 1: [self _setHomePage]; break;
5454
5455 case 2: [self setPage:install_]; break;
5456 case 3: [self setPage:changes_]; break;
5457 case 4: [self setPage:manage_]; break;
5458 case 5: [self setPage:search_]; break;
5459
5460 default: _assert(false);
5461 }
5462
5463 tag_ = tag;
5464 }
5465
5466 - (void) fixSpringBoard {
5467 pid_t pid = ExecFork();
5468 if (pid == 0) {
5469 sleep(1);
5470
5471 if (pid_t child = fork()) {
5472 waitpid(child, NULL, 0);
5473 } else {
5474 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
5475 perror("launchctl unload");
5476 exit(0);
5477 }
5478
5479 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
5480 perror("launchctl load");
5481 exit(0);
5482 }
5483 }
5484
5485 - (void) applicationWillSuspend {
5486 [database_ clean];
5487
5488 if (reload_) {
5489 #ifndef __OBJC2__
5490 [self fixSpringBoard];
5491 #endif
5492 }
5493
5494 [super applicationWillSuspend];
5495 }
5496
5497 - (void) finish {
5498 if (hud_ != nil) {
5499 [self setStatusBarShowsProgress:NO];
5500
5501 [hud_ show:NO];
5502 [hud_ removeFromSuperview];
5503 [hud_ autorelease];
5504 hud_ = nil;
5505
5506 pid_t pid = ExecFork();
5507 if (pid == 0) {
5508 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
5509 perror("launchctl stop");
5510 }
5511
5512 return;
5513 }
5514
5515 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
5516
5517 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5518 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
5519 0, 0, screenrect.size.width, screenrect.size.height - 48
5520 ) database:database_];
5521
5522 [book_ setDelegate:self];
5523
5524 [overlay_ addSubview:book_];
5525
5526 NSArray *buttonitems = [NSArray arrayWithObjects:
5527 [NSDictionary dictionaryWithObjectsAndKeys:
5528 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5529 @"home-up.png", kUIButtonBarButtonInfo,
5530 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
5531 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
5532 self, kUIButtonBarButtonTarget,
5533 @"Home", kUIButtonBarButtonTitle,
5534 @"0", kUIButtonBarButtonType,
5535 nil],
5536
5537 [NSDictionary dictionaryWithObjectsAndKeys:
5538 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5539 @"install-up.png", kUIButtonBarButtonInfo,
5540 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5541 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5542 self, kUIButtonBarButtonTarget,
5543 @"Sections", kUIButtonBarButtonTitle,
5544 @"0", kUIButtonBarButtonType,
5545 nil],
5546
5547 [NSDictionary dictionaryWithObjectsAndKeys:
5548 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5549 @"changes-up.png", kUIButtonBarButtonInfo,
5550 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
5551 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5552 self, kUIButtonBarButtonTarget,
5553 @"Changes", kUIButtonBarButtonTitle,
5554 @"0", kUIButtonBarButtonType,
5555 nil],
5556
5557 [NSDictionary dictionaryWithObjectsAndKeys:
5558 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5559 @"manage-up.png", kUIButtonBarButtonInfo,
5560 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
5561 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5562 self, kUIButtonBarButtonTarget,
5563 @"Manage", kUIButtonBarButtonTitle,
5564 @"0", kUIButtonBarButtonType,
5565 nil],
5566
5567 [NSDictionary dictionaryWithObjectsAndKeys:
5568 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
5569 @"search-up.png", kUIButtonBarButtonInfo,
5570 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
5571 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5572 self, kUIButtonBarButtonTarget,
5573 @"Search", kUIButtonBarButtonTitle,
5574 @"0", kUIButtonBarButtonType,
5575 nil],
5576 nil];
5577
5578 buttonbar_ = [[UIButtonBar alloc]
5579 initInView:overlay_
5580 withFrame:CGRectMake(
5581 0, screenrect.size.height - ButtonBarHeight_,
5582 screenrect.size.width, ButtonBarHeight_
5583 )
5584 withItemList:buttonitems
5585 ];
5586
5587 [buttonbar_ setDelegate:self];
5588 [buttonbar_ setBarStyle:1];
5589 [buttonbar_ setButtonBarTrackingMode:2];
5590
5591 int buttons[5] = {1, 2, 3, 4, 5};
5592 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5593 [buttonbar_ showButtonGroup:0 withDuration:0];
5594
5595 for (int i = 0; i != 5; ++i)
5596 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
5597 i * 64 + 2, 1, 60, ButtonBarHeight_
5598 )];
5599
5600 [buttonbar_ showSelectionForButton:1];
5601 [overlay_ addSubview:buttonbar_];
5602
5603 [UIKeyboard initImplementationNow];
5604 CGSize keysize = [UIKeyboard defaultSize];
5605 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
5606 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
5607 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
5608 [overlay_ addSubview:keyboard_];
5609
5610 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5611 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5612 manage_ = [[ManageView alloc] initWithBook:book_ database:database_];
5613 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5614
5615 if (!bootstrap_)
5616 [underlay_ addSubview:overlay_];
5617
5618 [self reloadData];
5619
5620 if (bootstrap_)
5621 [self bootstrap];
5622 else
5623 [self _setHomePage];
5624 }
5625
5626 - (void) reorganize {
5627 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5628 system("/usr/libexec/cydia/free.sh");
5629 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
5630 [pool release];
5631 }
5632
5633 - (void) applicationSuspend:(__GSEvent *)event {
5634 if (hud_ == nil && ![progress_ isRunning])
5635 [super applicationSuspend:event];
5636 }
5637
5638 - (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
5639 if (hud_ == nil)
5640 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
5641 }
5642
5643 - (void) _setSuspended:(BOOL)value {
5644 if (hud_ == nil)
5645 [super _setSuspended:value];
5646 }
5647
5648 - (void) applicationDidFinishLaunching:(id)unused {
5649 _assert(pkgInitConfig(*_config));
5650 _assert(pkgInitSystem(*_config, _system));
5651
5652 confirm_ = nil;
5653 tag_ = 1;
5654
5655 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
5656 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
5657
5658 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
5659 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
5660
5661 [window_ orderFront: self];
5662 [window_ makeKey: self];
5663 [window_ _setHidden: NO];
5664
5665 database_ = [[Database alloc] init];
5666 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
5667 [database_ setDelegate:progress_];
5668 [window_ setContentView:progress_];
5669
5670 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
5671 [progress_ setContentView:underlay_];
5672
5673 [progress_ resetView];
5674
5675 if (
5676 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
5677 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
5678 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
5679 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
5680 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
5681 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL
5682 ) {
5683 [self setIdleTimerDisabled:YES];
5684
5685 hud_ = [[UIProgressHUD alloc] initWithWindow:window_];
5686 [hud_ setText:@"Reorganizing\n\nWill Automatically\nRestart When Done"];
5687 [hud_ show:YES];
5688 [underlay_ addSubview:hud_];
5689
5690 [self setStatusBarShowsProgress:YES];
5691
5692 [NSThread
5693 detachNewThreadSelector:@selector(reorganize)
5694 toTarget:self
5695 withObject:nil
5696 ];
5697 } else
5698 [self finish];
5699 }
5700
5701 - (void) showKeyboard:(BOOL)show {
5702 CGSize keysize = [UIKeyboard defaultSize];
5703 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
5704 CGRect keyup = keydown;
5705 keyup.origin.y -= keysize.height;
5706
5707 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
5708 [animation setSignificantRectFields:2];
5709
5710 if (show) {
5711 [animation setStartFrame:keydown];
5712 [animation setEndFrame:keyup];
5713 [keyboard_ activate];
5714 } else {
5715 [animation setStartFrame:keyup];
5716 [animation setEndFrame:keydown];
5717 [keyboard_ deactivate];
5718 }
5719
5720 [[UIAnimator sharedAnimator]
5721 addAnimations:[NSArray arrayWithObjects:animation, nil]
5722 withDuration:KeyboardTime_
5723 start:YES
5724 ];
5725 }
5726
5727 - (void) slideUp:(UIAlertSheet *)alert {
5728 if (Advanced_)
5729 [alert presentSheetFromButtonBar:buttonbar_];
5730 else
5731 [alert presentSheetInView:overlay_];
5732 }
5733
5734 @end
5735
5736 void AddPreferences(NSString *plist) {
5737 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5738
5739 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
5740 _assert(settings != NULL);
5741 NSMutableArray *items = [settings objectForKey:@"items"];
5742
5743 bool cydia(false);
5744
5745 for (size_t i(0); i != [items count]; ++i) {
5746 NSMutableDictionary *item([items objectAtIndex:i]);
5747 NSString *label = [item objectForKey:@"label"];
5748 if (label != nil && [label isEqualToString:@"Cydia"]) {
5749 cydia = true;
5750 break;
5751 }
5752 }
5753
5754 if (!cydia) {
5755 for (size_t i(0); i != [items count]; ++i) {
5756 NSDictionary *item([items objectAtIndex:i]);
5757 NSString *label = [item objectForKey:@"label"];
5758 if (label != nil && [label isEqualToString:@"General"]) {
5759 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
5760 @"CydiaSettings", @"bundle",
5761 @"PSLinkCell", @"cell",
5762 [NSNumber numberWithBool:YES], @"hasIcon",
5763 [NSNumber numberWithBool:YES], @"isController",
5764 @"Cydia", @"label",
5765 nil] atIndex:(i + 1)];
5766
5767 break;
5768 }
5769 }
5770
5771 _assert([settings writeToFile:plist atomically:YES] == YES);
5772 }
5773
5774 [pool release];
5775 }
5776
5777 /*IMP alloc_;
5778 id Alloc_(id self, SEL selector) {
5779 id object = alloc_(self, selector);
5780 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
5781 return object;
5782 }*/
5783
5784 /*IMP dealloc_;
5785 id Dealloc_(id self, SEL selector) {
5786 id object = dealloc_(self, selector);
5787 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
5788 return object;
5789 }*/
5790
5791 int main(int argc, char *argv[]) {
5792 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5793
5794 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
5795
5796 Home_ = NSHomeDirectory();
5797
5798 {
5799 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
5800 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
5801 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
5802 Sounds_Keyboard_ = [keyboard boolValue];
5803 }
5804
5805 setuid(0);
5806 setgid(0);
5807
5808 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
5809 _assert(errno == ENOENT);
5810 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
5811 _assert(errno == ENOENT);
5812
5813 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
5814 alloc_ = alloc->method_imp;
5815 alloc->method_imp = (IMP) &Alloc_;*/
5816
5817 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
5818 dealloc_ = dealloc->method_imp;
5819 dealloc->method_imp = (IMP) &Dealloc_;*/
5820
5821 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
5822 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5823 Firmware_ = strdup([prover UTF8String]);
5824 NSArray *versions = [prover componentsSeparatedByString:@"."];
5825 int count = [versions count];
5826 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
5827 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
5828 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
5829 }
5830 }
5831
5832 size_t size;
5833 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
5834 char *machine = new char[size];
5835 sysctlbyname("hw.machine", machine, &size, NULL, 0);
5836 Machine_ = machine;
5837
5838 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
5839 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
5840 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
5841 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
5842 CFRelease(serial);
5843 }
5844
5845 IOObjectRelease(service);
5846 }
5847
5848 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
5849 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
5850
5851 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
5852 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
5853 else {
5854 Packages_ = [Metadata_ objectForKey:@"Packages"];
5855 Sections_ = [Metadata_ objectForKey:@"Sections"];
5856 }
5857
5858 if (access("/User", F_OK) != 0)
5859 system("/usr/libexec/cydia/firmware.sh");
5860
5861 Locale_ = CFLocaleCopyCurrent();
5862 space_ = CGColorSpaceCreateDeviceRGB();
5863
5864 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
5865 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
5866 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
5867 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
5868 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
5869
5870 int value = UIApplicationMain(argc, argv, [Cydia class]);
5871
5872 CGColorSpaceRelease(space_);
5873 CFRelease(Locale_);
5874
5875 [pool release];
5876 return value;
5877 }