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