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