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