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