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