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