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