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