]> git.saurik.com Git - cydia.git/blame - Cydia.mm
Rewrote the makefile.
[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 {{{ */
807ae6d7
JF
39#import "UICaboodle.h"
40
ab398adf
JF
41#include <objc/objc.h>
42#include <objc/runtime.h>
43
5f6bff8c 44#include <CoreGraphics/CoreGraphics.h>
9743c015 45#include <GraphicsServices/GraphicsServices.h>
5f6bff8c 46#include <Foundation/Foundation.h>
7d2ac47f 47
7d2ac47f 48#import <QuartzCore/CALayer.h>
36bb2ca2 49
baf80942 50#import <UIKit/UIKit.h>
a9543575
JF
51
52// XXX: remove
c390d3ab 53#import <MessageUI/MailComposeController.h>
7e9a36b6 54
680eb135
JF
55#include <WebKit/DOMCSSPrimitiveValue.h>
56#include <WebKit/DOMCSSStyleDeclaration.h>
57#include <WebKit/DOMDocument.h>
58#include <WebKit/DOMHTMLBodyElement.h>
59#include <WebKit/DOMNodeList.h>
60#include <WebKit/DOMRGBColor.h>
affeffc7 61
36bb2ca2 62#include <WebKit/WebFrame.h>
affeffc7 63#include <WebKit/WebPolicyDelegate.h>
fd7853a6 64#include <WebKit/WebPreferences.h>
680eb135 65#include <WebKit/WebScriptObject.h>
9743c015 66
680eb135 67#import <WebKit/WebView.h>
affeffc7
JF
68#import <WebKit/WebView-WebPrivate.h>
69
fd7853a6
JF
70#include <WebCore/Page.h>
71#include <WebCore/Settings.h>
72
9fe5e5f8
JF
73#import <JavaScriptCore/JavaScriptCore.h>
74
2367a917 75#include <sstream>
a0be02eb
JF
76#include <string>
77
2367a917
JF
78#include <ext/stdio_filebuf.h>
79
dc5812ec
JF
80#include <apt-pkg/acquire.h>
81#include <apt-pkg/acquire-item.h>
82#include <apt-pkg/algorithms.h>
83#include <apt-pkg/cachefile.h>
77fcccaf 84#include <apt-pkg/clean.h>
dc5812ec 85#include <apt-pkg/configuration.h>
3178d79b 86#include <apt-pkg/debmetaindex.h>
dc5812ec
JF
87#include <apt-pkg/error.h>
88#include <apt-pkg/init.h>
dddbc481 89#include <apt-pkg/mmap.h>
dc5812ec 90#include <apt-pkg/pkgrecords.h>
dddbc481 91#include <apt-pkg/sha1.h>
dc5812ec 92#include <apt-pkg/sourcelist.h>
2367a917 93#include <apt-pkg/sptr.h>
affeffc7 94#include <apt-pkg/strutl.h>
dc5812ec 95
87f46a96
JF
96#include <sys/types.h>
97#include <sys/stat.h>
3178d79b 98#include <sys/sysctl.h>
87f46a96 99
2a8d9add 100#include <notify.h>
dddbc481 101#include <dlfcn.h>
3178d79b 102
b4d89997
JF
103extern "C" {
104#include <mach-o/nlist.h>
105}
106
a0be02eb
JF
107#include <cstdio>
108#include <cstdlib>
109#include <cstring>
b4d89997 110
2367a917 111#include <errno.h>
dc5812ec 112#include <pcre.h>
a9543575 113
a9543575
JF
114#import "BrowserView.h"
115#import "ResetView.h"
fd7853a6
JF
116
117#import "substrate.h"
dc5812ec 118/* }}} */
3178d79b 119
affeffc7
JF
120//#define _finline __attribute__((force_inline))
121#define _finline inline
90515f88 122
807ae6d7
JF
123struct timeval _ltv;
124bool _itv;
125
126#define _limit(count) do { \
127 static size_t _count(0); \
128 if (++_count == count) \
129 exit(0); \
130} while (false)
131
2083b866
JF
132static uint64_t profile_;
133
134#define _timestamp ({ \
135 struct timeval tv; \
136 gettimeofday(&tv, NULL); \
137 tv.tv_sec * 1000000 + tv.tv_usec; \
138})
139
affeffc7
JF
140/* Objective-C Handle<> {{{ */
141template <typename Type_>
142class _H {
143 typedef _H<Type_> This_;
1e7a90f5 144
affeffc7
JF
145 private:
146 Type_ *value_;
1e7a90f5 147
affeffc7
JF
148 _finline void Retain_() {
149 if (value_ != nil)
150 [value_ retain];
151 }
1e7a90f5 152
affeffc7
JF
153 _finline void Clear_() {
154 if (value_ != nil)
155 [value_ release];
156 }
1e7a90f5 157
affeffc7
JF
158 public:
159 _finline _H(Type_ *value = NULL, bool mended = false) :
160 value_(value)
161 {
162 if (!mended)
163 Retain_();
164 }
ffdee4ec 165
affeffc7
JF
166 _finline ~_H() {
167 Clear_();
168 }
1e7a90f5 169
affeffc7
JF
170 _finline This_ &operator =(Type_ *value) {
171 if (value_ != value) {
172 Clear_();
173 value_ = value;
174 Retain_();
175 } return this;
176 }
177};
178/* }}} */
179
180#define _pooled _H<NSAutoreleasePool> _pool([[NSAutoreleasePool alloc] init], true);
181
bf8476c8
JF
182void NSLogPoint(const char *fix, const CGPoint &point) {
183 NSLog(@"%s(%g,%g)", fix, point.x, point.y);
184}
185
affeffc7
JF
186void NSLogRect(const char *fix, const CGRect &rect) {
187 NSLog(@"%s(%g,%g)+(%g,%g)", fix, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
1e7a90f5
JF
188}
189
68f1828e
JF
190/* NSForcedOrderingSearch doesn't work on the iPhone */
191static const NSStringCompareOptions BaseCompareOptions_ = NSNumericSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch;
192static const NSStringCompareOptions ForcedCompareOptions_ = BaseCompareOptions_;
193static const NSStringCompareOptions LaxCompareOptions_ = BaseCompareOptions_ | NSCaseInsensitiveSearch;
1e7a90f5 194
affeffc7
JF
195/* iPhoneOS 2.0 Compatibility {{{ */
196#ifdef __OBJC2__
1e7a90f5
JF
197@interface UITextView (iPhoneOS)
198- (void) setTextSize:(float)size;
199@end
200
201@implementation UITextView (iPhoneOS)
202
203- (void) setTextSize:(float)size {
204 [self setFont:[[self font] fontWithSize:size]];
205}
206
207@end
49048579 208#endif
1e7a90f5 209/* }}} */
49048579 210
c390d3ab
JF
211extern NSString * const kCAFilterNearest;
212
6e201c55
JF
213/* Information Dictionaries {{{ */
214@interface NSMutableArray (Cydia)
215- (void) addInfoDictionary:(NSDictionary *)info;
216@end
217
218@implementation NSMutableArray (Cydia)
219
220- (void) addInfoDictionary:(NSDictionary *)info {
221 [self addObject:info];
222}
223
224@end
225
226@interface NSMutableDictionary (Cydia)
227- (void) addInfoDictionary:(NSDictionary *)info;
228@end
229
230@implementation NSMutableDictionary (Cydia)
231
232- (void) addInfoDictionary:(NSDictionary *)info {
233 NSString *bundle = [info objectForKey:@"CFBundleIdentifier"];
234 [self setObject:info forKey:bundle];
235}
236
affeffc7
JF
237@end
238/* }}} */
239/* Pop Transitions {{{ */
240@interface PopTransitionView : UITransitionView {
241}
242
243@end
244
245@implementation PopTransitionView
246
247- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
248 if (from != nil && to == nil)
249 [self removeFromSuperview];
250}
251
252@end
253
254@interface UIView (PopUpView)
255- (void) popFromSuperviewAnimated:(BOOL)animated;
256- (void) popSubview:(UIView *)view;
257@end
258
259@implementation UIView (PopUpView)
260
261- (void) popFromSuperviewAnimated:(BOOL)animated {
262 [[self superview] transition:(animated ? UITransitionPushFromTop : UITransitionNone) toView:nil];
263}
264
265- (void) popSubview:(UIView *)view {
266 UITransitionView *transition([[[PopTransitionView alloc] initWithFrame:[self bounds]] autorelease]);
267 [transition setDelegate:transition];
268 [self addSubview:transition];
269
270 UIView *blank = [[[UIView alloc] initWithFrame:[transition bounds]] autorelease];
271 [transition transition:UITransitionNone toView:blank];
272 [transition transition:UITransitionPushFromBottom toView:view];
273}
274
6e201c55
JF
275@end
276/* }}} */
277
c390d3ab 278#define lprintf(args...) fprintf(stderr, args)
a9543575 279
fd7853a6 280#define ForRelease 1
680eb135 281#define ForSaurik (1 && !ForRelease)
affeffc7 282#define RecycleWebViews 0
680eb135 283#define AlwaysReload (1 && !ForRelease)
795d26fc 284
807ae6d7
JF
285/* Radix Sort {{{ */
286@interface NSMutableArray (Radix)
9487f027 287- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object;
807ae6d7
JF
288@end
289
290@implementation NSMutableArray (Radix)
291
9487f027 292- (void) radixSortUsingSelector:(SEL)selector withObject:(id)object {
807ae6d7
JF
293 NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[NSMethodSignature signatureWithObjCTypes:"L12@0:4@8"]]);
294 [invocation setSelector:selector];
295 [invocation setArgument:&object atIndex:2];
296
297 size_t count([self count]);
298
299 struct RadixItem {
300 size_t index;
301 uint32_t key;
302 } *swap(new RadixItem[count * 2]), *lhs(swap), *rhs(swap + count);
303
304 for (size_t i(0); i != count; ++i) {
305 RadixItem &item(lhs[i]);
306 item.index = i;
307
308 id object([self objectAtIndex:i]);
309 [invocation setTarget:object];
310
311 [invocation invoke];
312 [invocation getReturnValue:&item.key];
313 }
314
ec3f2f53 315 static const size_t width = 32;
807ae6d7
JF
316 static const size_t bits = 11;
317 static const size_t slots = 1 << bits;
ec3f2f53 318 static const size_t passes = (width + (bits - 1)) / bits;
807ae6d7
JF
319
320 size_t *hist(new size_t[slots]);
321
322 for (size_t pass(0); pass != passes; ++pass) {
323 memset(hist, 0, sizeof(size_t) * slots);
324
325 for (size_t i(0); i != count; ++i) {
326 uint32_t key(lhs[i].key);
327 key >>= pass * bits;
ec3f2f53 328 key &= _not(uint32_t) >> width - bits;
807ae6d7
JF
329 ++hist[key];
330 }
331
332 size_t offset(0);
333 for (size_t i(0); i != slots; ++i) {
334 size_t local(offset);
335 offset += hist[i];
336 hist[i] = local;
337 }
338
339 for (size_t i(0); i != count; ++i) {
340 uint32_t key(lhs[i].key);
341 key >>= pass * bits;
ec3f2f53 342 key &= _not(uint32_t) >> width - bits;
807ae6d7
JF
343 rhs[hist[key]++] = lhs[i];
344 }
345
346 RadixItem *tmp(lhs);
347 lhs = rhs;
348 rhs = tmp;
349 }
350
351 delete [] hist;
352
353 NSMutableArray *values([NSMutableArray arrayWithCapacity:count]);
354 for (size_t i(0); i != count; ++i)
355 [values addObject:[self objectAtIndex:lhs[i].index]];
356 [self setArray:values];
357
358 delete [] swap;
359}
360
361@end
362/* }}} */
363
bf8476c8
JF
364/* Apple Bug Fixes {{{ */
365@implementation UIWebDocumentView (Cydia)
366
367- (void) _setScrollerOffset:(CGPoint)offset {
368 UIScroller *scroller([self _scroller]);
369
370 CGSize size([scroller contentSize]);
371 CGSize bounds([scroller bounds].size);
372
373 CGPoint max;
374 max.x = size.width - bounds.width;
375 max.y = size.height - bounds.height;
376
377 // wtf Apple?!
378 if (max.x < 0)
379 max.x = 0;
380 if (max.y < 0)
381 max.y = 0;
382
383 offset.x = offset.x < 0 ? 0 : offset.x > max.x ? max.x : offset.x;
384 offset.y = offset.y < 0 ? 0 : offset.y > max.y ? max.y : offset.y;
385
386 [scroller setOffset:offset];
387}
388
389@end
390/* }}} */
391
31f3cfff
JF
392typedef enum {
393 kUIControlEventMouseDown = 1 << 0,
394 kUIControlEventMouseMovedInside = 1 << 2, // mouse moved inside control target
395 kUIControlEventMouseMovedOutside = 1 << 3, // mouse moved outside control target
396 kUIControlEventMouseUpInside = 1 << 6, // mouse up inside control target
397 kUIControlEventMouseUpOutside = 1 << 7, // mouse up outside control target
398 kUIControlAllEvents = (kUIControlEventMouseDown | kUIControlEventMouseMovedInside | kUIControlEventMouseMovedOutside | kUIControlEventMouseUpInside | kUIControlEventMouseUpOutside)
399} UIControlEventMasks;
400
680eb135
JF
401NSUInteger DOMNodeList$countByEnumeratingWithState$objects$count$(DOMNodeList *self, SEL sel, NSFastEnumerationState *state, id *objects, NSUInteger count) {
402 size_t length([self length] - state->state);
403 if (length <= 0)
404 return 0;
405 else if (length > count)
406 length = count;
407 for (size_t i(0); i != length; ++i)
408 objects[i] = [self item:state->state++];
409 state->itemsPtr = objects;
410 state->mutationsPtr = (unsigned long *) self;
411 return length;
412}
413
2388b078
JF
414@interface NSString (UIKit)
415- (NSString *) stringByAddingPercentEscapes;
416- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
417@end
418
419@interface NSString (Cydia)
420+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length;
a54b1c10 421- (NSComparisonResult) compareByPath:(NSString *)other;
2388b078
JF
422@end
423
424@implementation NSString (Cydia)
425
426+ (NSString *) stringWithUTF8Bytes:(const char *)bytes length:(int)length {
2083b866 427 return [[[NSString alloc] initWithBytes:bytes length:length encoding:NSUTF8StringEncoding] autorelease];
2388b078
JF
428}
429
a54b1c10
JF
430- (NSComparisonResult) compareByPath:(NSString *)other {
431 NSString *prefix = [self commonPrefixWithString:other options:0];
432 size_t length = [prefix length];
433
434 NSRange lrange = NSMakeRange(length, [self length] - length);
435 NSRange rrange = NSMakeRange(length, [other length] - length);
436
437 lrange = [self rangeOfString:@"/" options:0 range:lrange];
438 rrange = [other rangeOfString:@"/" options:0 range:rrange];
439
440 NSComparisonResult value;
441
442 if (lrange.location == NSNotFound && rrange.location == NSNotFound)
443 value = NSOrderedSame;
444 else if (lrange.location == NSNotFound)
445 value = NSOrderedAscending;
446 else if (rrange.location == NSNotFound)
447 value = NSOrderedDescending;
448 else
449 value = NSOrderedSame;
450
451 NSString *lpath = lrange.location == NSNotFound ? [self substringFromIndex:length] :
452 [self substringWithRange:NSMakeRange(length, lrange.location - length)];
453 NSString *rpath = rrange.location == NSNotFound ? [other substringFromIndex:length] :
454 [other substringWithRange:NSMakeRange(length, rrange.location - length)];
455
456 NSComparisonResult result = [lpath compare:rpath];
457 return result == NSOrderedSame ? value : result;
458}
459
2388b078
JF
460@end
461
b4d89997
JF
462/* Perl-Compatible RegEx {{{ */
463class Pcre {
464 private:
465 pcre *code_;
466 pcre_extra *study_;
467 int capture_;
468 int *matches_;
469 const char *data_;
470
471 public:
472 Pcre(const char *regex) :
473 study_(NULL)
474 {
475 const char *error;
476 int offset;
477 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
478
479 if (code_ == NULL) {
c390d3ab 480 lprintf("%d:%s\n", offset, error);
b4d89997
JF
481 _assert(false);
482 }
483
484 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
485 matches_ = new int[(capture_ + 1) * 3];
486 }
487
488 ~Pcre() {
489 pcre_free(code_);
490 delete matches_;
491 }
492
493 NSString *operator [](size_t match) {
2388b078 494 return [NSString stringWithUTF8Bytes:(data_ + matches_[match * 2]) length:(matches_[match * 2 + 1] - matches_[match * 2])];
b4d89997
JF
495 }
496
8b29f8e6
JF
497 bool operator ()(NSString *data) {
498 // XXX: length is for characters, not for bytes
499 return operator ()([data UTF8String], [data length]);
500 }
501
b4d89997
JF
502 bool operator ()(const char *data, size_t size) {
503 data_ = data;
504 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
505 }
506};
507/* }}} */
36bb2ca2 508/* Mime Addresses {{{ */
36bb2ca2
JF
509@interface Address : NSObject {
510 NSString *name_;
6f1a15d9 511 NSString *address_;
36bb2ca2
JF
512}
513
514- (NSString *) name;
6f1a15d9 515- (NSString *) address;
36bb2ca2
JF
516
517+ (Address *) addressWithString:(NSString *)string;
518- (Address *) initWithString:(NSString *)string;
519@end
520
521@implementation Address
522
523- (void) dealloc {
524 [name_ release];
6f1a15d9
JF
525 if (address_ != nil)
526 [address_ release];
36bb2ca2
JF
527 [super dealloc];
528}
529
530- (NSString *) name {
531 return name_;
532}
533
6f1a15d9
JF
534- (NSString *) address {
535 return address_;
36bb2ca2
JF
536}
537
538+ (Address *) addressWithString:(NSString *)string {
539 return [[[Address alloc] initWithString:string] autorelease];
540}
541
6f1a15d9
JF
542+ (NSArray *) _attributeKeys {
543 return [NSArray arrayWithObjects:@"address", @"name", nil];
544}
545
546- (NSArray *) attributeKeys {
547 return [[self class] _attributeKeys];
548}
549
550+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
551 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
552}
553
36bb2ca2
JF
554- (Address *) initWithString:(NSString *)string {
555 if ((self = [super init]) != nil) {
556 const char *data = [string UTF8String];
557 size_t size = [string length];
558
6f1a15d9 559 static Pcre address_r("^\"?(.*)\"? <([^>]*)>$");
7b0ce2da 560
6f1a15d9
JF
561 if (address_r(data, size)) {
562 name_ = [address_r[1] retain];
563 address_ = [address_r[2] retain];
36bb2ca2 564 } else {
affeffc7 565 name_ = [string retain];
6f1a15d9 566 address_ = nil;
36bb2ca2
JF
567 }
568 } return self;
569}
570
571@end
572/* }}} */
5f6bff8c 573/* CoreGraphics Primitives {{{ */
b4d89997
JF
574class CGColor {
575 private:
576 CGColorRef color_;
577
578 public:
36bb2ca2
JF
579 CGColor() :
580 color_(NULL)
581 {
582 }
583
584 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
585 color_(NULL)
586 {
587 Set(space, red, green, blue, alpha);
588 }
589
590 void Clear() {
591 if (color_ != NULL)
592 CGColorRelease(color_);
b4d89997
JF
593 }
594
595 ~CGColor() {
36bb2ca2
JF
596 Clear();
597 }
598
599 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
600 Clear();
601 float color[] = {red, green, blue, alpha};
602 color_ = CGColorCreate(space, color);
b4d89997
JF
603 }
604
605 operator CGColorRef() {
606 return color_;
607 }
608};
b4d89997
JF
609/* }}} */
610
f641a0e5
JF
611extern "C" void UISetColor(CGColorRef color);
612
36bb2ca2 613/* Random Global Variables {{{ */
3178d79b 614static const int PulseInterval_ = 50000;
2388b078 615static const int ButtonBarHeight_ = 48;
6d9712c4 616static const float KeyboardTime_ = 0.3f;
affeffc7
JF
617
618#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
619#define SandboxTemplate_ "/usr/share/sandbox/SandboxTemplate.sb"
2a8d9add 620
f641a0e5 621static CGColor Blue_;
77fcccaf 622static CGColor Blueish_;
36bb2ca2 623static CGColor Black_;
baf80942 624static CGColor Off_;
36bb2ca2 625static CGColor White_;
7b0ce2da 626static CGColor Gray_;
36bb2ca2 627
7d2ac47f 628static NSString *App_;
d73cede2
JF
629static NSString *Home_;
630static BOOL Sounds_Keyboard_;
631
2388b078 632static BOOL Advanced_;
c390d3ab 633#if !ForSaurik
6f1a15d9 634static BOOL Loaded_;
c390d3ab 635#endif
a54b1c10 636static BOOL Ignored_;
2388b078 637
f641a0e5
JF
638static UIFont *Font12_;
639static UIFont *Font12Bold_;
640static UIFont *Font14_;
641static UIFont *Font18Bold_;
642static UIFont *Font22Bold_;
643
87f46a96
JF
644static const char *Machine_ = NULL;
645static const NSString *UniqueID_ = NULL;
3178d79b 646
2a8d9add
JF
647unsigned Major_;
648unsigned Minor_;
649unsigned BugFix_;
650
199d0ba5 651CFLocaleRef Locale_;
36bb2ca2
JF
652CGColorSpaceRef space_;
653
2a8d9add 654#define FW_LEAST(major, minor, bugfix) \
51a1ffdf
JF
655 (major < Major_ || major == Major_ && \
656 (minor < Minor_ || minor == Minor_ && \
657 bugfix <= BugFix_))
2a8d9add 658
36bb2ca2 659bool bootstrap_;
77fcccaf 660bool reload_;
b31b87cc 661
46dbfd32 662static NSDictionary *SectionMap_;
b4d89997 663static NSMutableDictionary *Metadata_;
680eb135 664static NSMutableDictionary *Indices_;
7b0ce2da
JF
665static _transient NSMutableDictionary *Settings_;
666static _transient NSString *Role_;
667static _transient NSMutableDictionary *Packages_;
668static _transient NSMutableDictionary *Sections_;
669static _transient NSMutableDictionary *Sources_;
bbb879fb 670static bool Changed_;
b4d89997 671static NSDate *now_;
8da60fb7 672
affeffc7
JF
673#if RecycleWebViews
674static NSMutableArray *Documents_;
675#endif
676
23672b39
JF
677NSString *GetLastUpdate() {
678 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
679
680 if (update == nil)
681 return @"Never or Unknown";
682
199d0ba5 683 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
23672b39
JF
684 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
685
686 CFRelease(formatter);
23672b39
JF
687
688 return [(NSString *) formatted autorelease];
689}
36bb2ca2
JF
690/* }}} */
691/* Display Helpers {{{ */
692inline float Interpolate(float begin, float end, float fraction) {
693 return (end - begin) * fraction + begin;
694}
2367a917 695
8fe19fc1 696NSString *SizeString(double size) {
affeffc7
JF
697 bool negative = size < 0;
698 if (negative)
699 size = -size;
700
8fe19fc1
JF
701 unsigned power = 0;
702 while (size > 1024) {
703 size /= 1024;
704 ++power;
705 }
706
707 static const char *powers_[] = {"B", "kB", "MB", "GB"};
708
86316a91 709 return [NSString stringWithFormat:@"%s%.1f %s", (negative ? "-" : ""), size, powers_[power]];
8fe19fc1
JF
710}
711
31f3cfff
JF
712NSString *StripVersion(NSString *version) {
713 NSRange colon = [version rangeOfString:@":"];
714 if (colon.location != NSNotFound)
715 version = [version substringFromIndex:(colon.location + 1)];
716 return version;
717}
718
4cf4165e
JF
719NSString *Simplify(NSString *title) {
720 const char *data = [title UTF8String];
721 size_t size = [title length];
722
7b0ce2da
JF
723 static Pcre square_r("^\\[(.*)\\]$");
724 if (square_r(data, size))
725 return Simplify(square_r[1]);
726
727 static Pcre paren_r("^\\((.*)\\)$");
728 if (paren_r(data, size))
729 return Simplify(paren_r[1]);
730
731 static Pcre title_r("^(.*?) \\(.*\\)$");
4cf4165e 732 if (title_r(data, size))
7b0ce2da
JF
733 return Simplify(title_r[1]);
734
735 return title;
4cf4165e 736}
36bb2ca2
JF
737/* }}} */
738
6d9712c4
JF
739bool isSectionVisible(NSString *section) {
740 NSDictionary *metadata = [Sections_ objectForKey:section];
741 NSNumber *hidden = metadata == nil ? nil : [metadata objectForKey:@"Hidden"];
742 return hidden == nil || ![hidden boolValue];
743}
744
d36e83a3 745/* Delegate Prototypes {{{ */
36bb2ca2
JF
746@class Package;
747@class Source;
748
31f3cfff
JF
749@interface NSObject (ProgressDelegate)
750@end
751
752@implementation NSObject(ProgressDelegate)
753
754- (void) _setProgressError:(NSArray *)args {
755 [self performSelector:@selector(setProgressError:forPackage:)
756 withObject:[args objectAtIndex:0]
757 withObject:([args count] == 1 ? nil : [args objectAtIndex:1])
758 ];
759}
760
761@end
762
36bb2ca2 763@protocol ProgressDelegate
31f3cfff 764- (void) setProgressError:(NSString *)error forPackage:(NSString *)id;
36bb2ca2
JF
765- (void) setProgressTitle:(NSString *)title;
766- (void) setProgressPercent:(float)percent;
baf80942 767- (void) startProgress;
36bb2ca2 768- (void) addProgressOutput:(NSString *)output;
baf80942 769- (bool) isCancelling:(size_t)received;
36bb2ca2
JF
770@end
771
8b29f8e6 772@protocol ConfigurationDelegate
5a09ae08 773- (void) repairWithSelector:(SEL)selector;
8b29f8e6
JF
774- (void) setConfigurationData:(NSString *)data;
775@end
776
36bb2ca2
JF
777@protocol CydiaDelegate
778- (void) installPackage:(Package *)package;
779- (void) removePackage:(Package *)package;
a9543575 780- (void) slideUp:(UIActionSheet *)alert;
36bb2ca2 781- (void) distUpgrade;
6d9712c4 782- (void) updateData;
7b0ce2da
JF
783- (void) syncData;
784- (void) askForSettings;
785- (UIProgressHUD *) addProgressHUD;
c390d3ab
JF
786- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag;
787- (RVPage *) pageForPackage:(NSString *)name;
788- (void) openMailToURL:(NSURL *)url;
6565453a 789- (void) clearFirstResponder;
36bb2ca2 790@end
d36e83a3 791/* }}} */
b4d89997 792
dc5812ec
JF
793/* Status Delegation {{{ */
794class Status :
795 public pkgAcquireStatus
796{
797 private:
31f3cfff 798 _transient NSObject<ProgressDelegate> *delegate_;
dc5812ec
JF
799
800 public:
801 Status() :
802 delegate_(nil)
803 {
804 }
805
806 void setDelegate(id delegate) {
807 delegate_ = delegate;
808 }
809
810 virtual bool MediaChange(std::string media, std::string drive) {
811 return false;
812 }
813
814 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
dc5812ec
JF
815 }
816
817 virtual void Fetch(pkgAcquire::ItemDesc &item) {
9487f027 818 //NSString *name([NSString stringWithUTF8String:item.ShortDesc.c_str()]);
2388b078 819 [delegate_ setProgressTitle:[NSString stringWithUTF8String:("Downloading " + item.ShortDesc).c_str()]];
dc5812ec
JF
820 }
821
822 virtual void Done(pkgAcquire::ItemDesc &item) {
dc5812ec
JF
823 }
824
825 virtual void Fail(pkgAcquire::ItemDesc &item) {
1d80f6b9
JF
826 if (
827 item.Owner->Status == pkgAcquire::Item::StatIdle ||
828 item.Owner->Status == pkgAcquire::Item::StatDone
829 )
830 return;
831
affeffc7
JF
832 std::string &error(item.Owner->ErrorText);
833 if (error.empty())
834 return;
835
836 NSString *description([NSString stringWithUTF8String:item.Description.c_str()]);
837 NSArray *fields([description componentsSeparatedByString:@" "]);
838 NSString *source([fields count] == 0 ? nil : [fields objectAtIndex:0]);
839
31f3cfff 840 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
affeffc7
JF
841 withObject:[NSArray arrayWithObjects:
842 [NSString stringWithUTF8String:error.c_str()],
843 source,
844 nil]
31f3cfff
JF
845 waitUntilDone:YES
846 ];
dc5812ec
JF
847 }
848
849 virtual bool Pulse(pkgAcquire *Owner) {
2367a917
JF
850 bool value = pkgAcquireStatus::Pulse(Owner);
851
852 float percent(
853 double(CurrentBytes + CurrentItems) /
854 double(TotalBytes + TotalItems)
855 );
856
36bb2ca2 857 [delegate_ setProgressPercent:percent];
baf80942 858 return [delegate_ isCancelling:CurrentBytes] ? false : value;
dc5812ec
JF
859 }
860
861 virtual void Start() {
baf80942 862 [delegate_ startProgress];
dc5812ec
JF
863 }
864
865 virtual void Stop() {
dc5812ec
JF
866 }
867};
868/* }}} */
869/* Progress Delegation {{{ */
870class Progress :
871 public OpProgress
872{
873 private:
36bb2ca2 874 _transient id<ProgressDelegate> delegate_;
dc5812ec
JF
875
876 protected:
877 virtual void Update() {
2388b078 878 [delegate_ setProgressTitle:[NSString stringWithUTF8String:Op.c_str()]];
36bb2ca2 879 [delegate_ setProgressPercent:(Percent / 100)];
dc5812ec
JF
880 }
881
882 public:
883 Progress() :
884 delegate_(nil)
885 {
886 }
887
888 void setDelegate(id delegate) {
889 delegate_ = delegate;
890 }
891
892 virtual void Done() {
36bb2ca2 893 [delegate_ setProgressPercent:1];
dc5812ec
JF
894 }
895};
896/* }}} */
897
36bb2ca2
JF
898/* Database Interface {{{ */
899@interface Database : NSObject {
900 pkgCacheFile cache_;
5a09ae08 901 pkgDepCache::Policy *policy_;
36bb2ca2
JF
902 pkgRecords *records_;
903 pkgProblemResolver *resolver_;
904 pkgAcquire *fetcher_;
905 FileFd *lock_;
906 SPtr<pkgPackageManager> manager_;
907 pkgSourceList *list_;
5f6bff8c 908
36bb2ca2
JF
909 NSMutableDictionary *sources_;
910 NSMutableArray *packages_;
b4d89997 911
31f3cfff 912 _transient NSObject<ConfigurationDelegate, ProgressDelegate> *delegate_;
36bb2ca2
JF
913 Status status_;
914 Progress progress_;
8b29f8e6 915
77fcccaf 916 int cydiafd_;
36bb2ca2 917 int statusfd_;
8b29f8e6 918 FILE *input_;
dc5812ec
JF
919}
920
770f2a8e
JF
921+ (Database *) sharedInstance;
922
77fcccaf 923- (void) _readCydia:(NSNumber *)fd;
36bb2ca2
JF
924- (void) _readStatus:(NSNumber *)fd;
925- (void) _readOutput:(NSNumber *)fd;
2367a917 926
8b29f8e6
JF
927- (FILE *) input;
928
36bb2ca2 929- (Package *) packageWithName:(NSString *)name;
dc5812ec 930
36bb2ca2 931- (pkgCacheFile &) cache;
5a09ae08 932- (pkgDepCache::Policy *) policy;
36bb2ca2
JF
933- (pkgRecords *) records;
934- (pkgProblemResolver *) resolver;
935- (pkgAcquire &) fetcher;
936- (NSArray *) packages;
7b0ce2da 937- (NSArray *) sources;
36bb2ca2
JF
938- (void) reloadData;
939
5a09ae08 940- (void) configure;
36bb2ca2
JF
941- (void) prepare;
942- (void) perform;
943- (void) upgrade;
944- (void) update;
945
946- (void) updateWithStatus:(Status &)status;
947
948- (void) setDelegate:(id)delegate;
949- (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
dc5812ec 950@end
36bb2ca2 951/* }}} */
dc5812ec 952
36bb2ca2
JF
953/* Source Class {{{ */
954@interface Source : NSObject {
955 NSString *description_;
956 NSString *label_;
957 NSString *origin_;
dc5812ec 958
36bb2ca2
JF
959 NSString *uri_;
960 NSString *distribution_;
961 NSString *type_;
962 NSString *version_;
2367a917 963
36bb2ca2 964 NSString *defaultIcon_;
dc5812ec 965
7b0ce2da 966 NSDictionary *record_;
36bb2ca2 967 BOOL trusted_;
dc5812ec
JF
968}
969
36bb2ca2 970- (Source *) initWithMetaIndex:(metaIndex *)index;
dc5812ec 971
7b0ce2da
JF
972- (NSComparisonResult) compareByNameAndType:(Source *)source;
973
974- (NSDictionary *) record;
36bb2ca2 975- (BOOL) trusted;
dc5812ec 976
36bb2ca2
JF
977- (NSString *) uri;
978- (NSString *) distribution;
979- (NSString *) type;
7b0ce2da
JF
980- (NSString *) key;
981- (NSString *) host;
36bb2ca2 982
7b0ce2da 983- (NSString *) name;
36bb2ca2
JF
984- (NSString *) description;
985- (NSString *) label;
986- (NSString *) origin;
987- (NSString *) version;
dc5812ec 988
36bb2ca2 989- (NSString *) defaultIcon;
7b0ce2da 990
dc5812ec 991@end
dc5812ec 992
36bb2ca2 993@implementation Source
dc5812ec 994
36bb2ca2
JF
995- (void) dealloc {
996 [uri_ release];
997 [distribution_ release];
998 [type_ release];
dc5812ec 999
36bb2ca2
JF
1000 if (description_ != nil)
1001 [description_ release];
1002 if (label_ != nil)
1003 [label_ release];
1004 if (origin_ != nil)
1005 [origin_ release];
1006 if (version_ != nil)
1007 [version_ release];
1008 if (defaultIcon_ != nil)
1009 [defaultIcon_ release];
7b0ce2da
JF
1010 if (record_ != nil)
1011 [record_ release];
dc5812ec 1012
36bb2ca2 1013 [super dealloc];
dc5812ec
JF
1014}
1015
6f1a15d9
JF
1016+ (NSArray *) _attributeKeys {
1017 return [NSArray arrayWithObjects:@"description", @"distribution", @"host", @"key", @"label", @"name", @"origin", @"trusted", @"type", @"uri", @"version", nil];
1018}
1019
1020- (NSArray *) attributeKeys {
1021 return [[self class] _attributeKeys];
1022}
1023
1024+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1025 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1026}
1027
36bb2ca2
JF
1028- (Source *) initWithMetaIndex:(metaIndex *)index {
1029 if ((self = [super init]) != nil) {
1030 trusted_ = index->IsTrusted();
dc5812ec 1031
2388b078
JF
1032 uri_ = [[NSString stringWithUTF8String:index->GetURI().c_str()] retain];
1033 distribution_ = [[NSString stringWithUTF8String:index->GetDist().c_str()] retain];
1034 type_ = [[NSString stringWithUTF8String:index->GetType()] retain];
dc5812ec 1035
36bb2ca2
JF
1036 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
1037 if (dindex != NULL) {
1038 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
1039 std::string line;
1040 while (std::getline(release, line)) {
1041 std::string::size_type colon(line.find(':'));
1042 if (colon == std::string::npos)
1043 continue;
dc5812ec 1044
36bb2ca2
JF
1045 std::string name(line.substr(0, colon));
1046 std::string value(line.substr(colon + 1));
1047 while (!value.empty() && value[0] == ' ')
1048 value = value.substr(1);
dc5812ec 1049
36bb2ca2 1050 if (name == "Default-Icon")
2388b078 1051 defaultIcon_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 1052 else if (name == "Description")
2388b078 1053 description_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 1054 else if (name == "Label")
2388b078 1055 label_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 1056 else if (name == "Origin")
2388b078 1057 origin_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2 1058 else if (name == "Version")
2388b078 1059 version_ = [[NSString stringWithUTF8String:value.c_str()] retain];
36bb2ca2
JF
1060 }
1061 }
7b0ce2da
JF
1062
1063 record_ = [Sources_ objectForKey:[self key]];
1064 if (record_ != nil)
1065 record_ = [record_ retain];
36bb2ca2 1066 } return self;
2367a917 1067}
dc5812ec 1068
7b0ce2da
JF
1069- (NSComparisonResult) compareByNameAndType:(Source *)source {
1070 NSDictionary *lhr = [self record];
1071 NSDictionary *rhr = [source record];
1072
1073 if (lhr != rhr)
1074 return lhr == nil ? NSOrderedDescending : NSOrderedAscending;
1075
1076 NSString *lhs = [self name];
1077 NSString *rhs = [source name];
1078
1079 if ([lhs length] != 0 && [rhs length] != 0) {
1080 unichar lhc = [lhs characterAtIndex:0];
1081 unichar rhc = [rhs characterAtIndex:0];
1082
1083 if (isalpha(lhc) && !isalpha(rhc))
1084 return NSOrderedAscending;
1085 else if (!isalpha(lhc) && isalpha(rhc))
1086 return NSOrderedDescending;
1087 }
1088
68f1828e 1089 return [lhs compare:rhs options:LaxCompareOptions_];
7b0ce2da
JF
1090}
1091
1092- (NSDictionary *) record {
1093 return record_;
1094}
1095
36bb2ca2
JF
1096- (BOOL) trusted {
1097 return trusted_;
1098}
3178d79b 1099
36bb2ca2
JF
1100- (NSString *) uri {
1101 return uri_;
1102}
ec97ef06 1103
36bb2ca2
JF
1104- (NSString *) distribution {
1105 return distribution_;
1106}
dc5812ec 1107
36bb2ca2
JF
1108- (NSString *) type {
1109 return type_;
dc5812ec
JF
1110}
1111
7b0ce2da
JF
1112- (NSString *) key {
1113 return [NSString stringWithFormat:@"%@:%@:%@", type_, uri_, distribution_];
1114}
1115
1116- (NSString *) host {
1117 return [[[NSURL URLWithString:[self uri]] host] lowercaseString];
1118}
1119
1120- (NSString *) name {
1121 return origin_ == nil ? [self host] : origin_;
1122}
1123
36bb2ca2
JF
1124- (NSString *) description {
1125 return description_;
1126}
b4d89997 1127
36bb2ca2 1128- (NSString *) label {
7b0ce2da 1129 return label_ == nil ? [self host] : label_;
36bb2ca2 1130}
3178d79b 1131
36bb2ca2
JF
1132- (NSString *) origin {
1133 return origin_;
1134}
3178d79b 1135
36bb2ca2
JF
1136- (NSString *) version {
1137 return version_;
1138}
2367a917 1139
36bb2ca2
JF
1140- (NSString *) defaultIcon {
1141 return defaultIcon_;
1142}
2367a917 1143
2388b078
JF
1144@end
1145/* }}} */
1146/* Relationship Class {{{ */
1147@interface Relationship : NSObject {
1148 NSString *type_;
1149 NSString *id_;
1150}
1151
1152- (NSString *) type;
1153- (NSString *) id;
1154- (NSString *) name;
1155
1156@end
1157
1158@implementation Relationship
1159
1160- (void) dealloc {
1161 [type_ release];
1162 [id_ release];
1163 [super dealloc];
1164}
1165
1166- (NSString *) type {
1167 return type_;
1168}
1169
1170- (NSString *) id {
1171 return id_;
1172}
1173
1174- (NSString *) name {
1175 _assert(false);
1176 return nil;
1177}
1178
dc5812ec 1179@end
b4d89997 1180/* }}} */
36bb2ca2 1181/* Package Class {{{ */
36bb2ca2
JF
1182@interface Package : NSObject {
1183 pkgCache::PkgIterator iterator_;
1184 _transient Database *database_;
1185 pkgCache::VerIterator version_;
1186 pkgCache::VerFileIterator file_;
bbb879fb 1187
36bb2ca2 1188 Source *source_;
bbb879fb 1189 bool cached_;
dc5812ec 1190
807ae6d7
JF
1191 NSString *section_;
1192
36bb2ca2
JF
1193 NSString *latest_;
1194 NSString *installed_;
dc5812ec 1195
36bb2ca2
JF
1196 NSString *id_;
1197 NSString *name_;
1198 NSString *tagline_;
1199 NSString *icon_;
25a2158d 1200 NSString *depiction_;
a9a0661e 1201 NSString *homepage_;
795d26fc 1202 Address *sponsor_;
77fcccaf 1203 Address *author_;
6d9712c4 1204 NSArray *tags_;
7b0ce2da 1205 NSString *role_;
2388b078
JF
1206
1207 NSArray *relationships_;
b4d89997
JF
1208}
1209
5a09ae08 1210- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
36bb2ca2 1211+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
b4d89997 1212
2388b078
JF
1213- (pkgCache::PkgIterator) iterator;
1214
36bb2ca2 1215- (NSString *) section;
dec6029f
JF
1216- (NSString *) simpleSection;
1217
36bb2ca2
JF
1218- (Address *) maintainer;
1219- (size_t) size;
1220- (NSString *) description;
1221- (NSString *) index;
dc5812ec 1222
807ae6d7 1223- (NSMutableDictionary *) metadata;
36bb2ca2 1224- (NSDate *) seen;
807ae6d7
JF
1225- (BOOL) subscribed;
1226- (BOOL) ignored;
9e98e020 1227
36bb2ca2
JF
1228- (NSString *) latest;
1229- (NSString *) installed;
5a09ae08
JF
1230
1231- (BOOL) valid;
31f3cfff 1232- (BOOL) upgradableAndEssential:(BOOL)essential;
36bb2ca2
JF
1233- (BOOL) essential;
1234- (BOOL) broken;
7d2ac47f 1235- (BOOL) unfiltered;
6d9712c4 1236- (BOOL) visible;
b4d89997 1237
9bedffaa
JF
1238- (BOOL) half;
1239- (BOOL) halfConfigured;
1240- (BOOL) halfInstalled;
1241- (BOOL) hasMode;
1242- (NSString *) mode;
1243
36bb2ca2
JF
1244- (NSString *) id;
1245- (NSString *) name;
1246- (NSString *) tagline;
770f2a8e 1247- (UIImage *) icon;
6f1a15d9 1248- (NSString *) homepage;
25a2158d 1249- (NSString *) depiction;
77fcccaf 1250- (Address *) author;
b4d89997 1251
affeffc7 1252- (NSArray *) files;
2388b078 1253- (NSArray *) relationships;
affeffc7
JF
1254- (NSArray *) warnings;
1255- (NSArray *) applications;
2388b078 1256
36bb2ca2 1257- (Source *) source;
7b0ce2da 1258- (NSString *) role;
680eb135 1259- (NSString *) rating;
b4d89997 1260
36bb2ca2 1261- (BOOL) matches:(NSString *)text;
b4d89997 1262
7b0ce2da 1263- (bool) hasSupportingRole;
6d9712c4 1264- (BOOL) hasTag:(NSString *)tag;
c390d3ab 1265- (NSString *) primaryPurpose;
770f2a8e 1266- (NSArray *) purposes;
6d9712c4 1267
36bb2ca2
JF
1268- (NSComparisonResult) compareByName:(Package *)package;
1269- (NSComparisonResult) compareBySection:(Package *)package;
807ae6d7
JF
1270
1271- (uint32_t) compareForChanges;
b4d89997 1272
36bb2ca2
JF
1273- (void) install;
1274- (void) remove;
06aa974d 1275
7d2ac47f 1276- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search;
440c215d 1277- (NSNumber *) isInstalledAndVisible:(NSNumber *)number;
7b0ce2da
JF
1278- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)section;
1279- (NSNumber *) isVisibleInSource:(Source *)source;
b4d89997 1280
36bb2ca2 1281@end
b4d89997 1282
36bb2ca2 1283@implementation Package
b4d89997 1284
36bb2ca2 1285- (void) dealloc {
bbb879fb
JF
1286 if (source_ != nil)
1287 [source_ release];
1288
807ae6d7
JF
1289 if (section_ != nil)
1290 [section_ release];
1291
36bb2ca2
JF
1292 [latest_ release];
1293 if (installed_ != nil)
1294 [installed_ release];
b4d89997 1295
36bb2ca2
JF
1296 [id_ release];
1297 if (name_ != nil)
1298 [name_ release];
1299 [tagline_ release];
1300 if (icon_ != nil)
1301 [icon_ release];
25a2158d
JF
1302 if (depiction_ != nil)
1303 [depiction_ release];
a9a0661e
JF
1304 if (homepage_ != nil)
1305 [homepage_ release];
795d26fc
JF
1306 if (sponsor_ != nil)
1307 [sponsor_ release];
77fcccaf
JF
1308 if (author_ != nil)
1309 [author_ release];
6d9712c4
JF
1310 if (tags_ != nil)
1311 [tags_ release];
7b0ce2da
JF
1312 if (role_ != nil)
1313 [role_ release];
9e98e020 1314
2388b078
JF
1315 if (relationships_ != nil)
1316 [relationships_ release];
1317
36bb2ca2 1318 [super dealloc];
b4d89997
JF
1319}
1320
6f1a15d9 1321+ (NSArray *) _attributeKeys {
680eb135 1322 return [NSArray arrayWithObjects:@"applications", @"author", @"depiction", @"description", @"essential", @"homepage", @"icon", @"id", @"installed", @"latest", @"maintainer", @"name", @"purposes", @"rating", @"section", @"size", @"source", @"sponsor", @"tagline", @"warnings", nil];
6f1a15d9
JF
1323}
1324
1325- (NSArray *) attributeKeys {
1326 return [[self class] _attributeKeys];
1327}
1328
1329+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
1330 return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
1331}
1332
5a09ae08 1333- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
36bb2ca2
JF
1334 if ((self = [super init]) != nil) {
1335 iterator_ = iterator;
1336 database_ = database;
b4d89997 1337
5a09ae08 1338 version_ = [database_ policy]->GetCandidateVer(iterator_);
a4b0ec52 1339 NSString *latest = version_.end() ? nil : [NSString stringWithUTF8String:version_.VerStr()];
9487f027 1340 latest_ = latest == nil ? nil : [StripVersion(latest) retain];
a4b0ec52
JF
1341
1342 pkgCache::VerIterator current = iterator_.CurrentVer();
1343 NSString *installed = current.end() ? nil : [NSString stringWithUTF8String:current.VerStr()];
1344 installed_ = [StripVersion(installed) retain];
bbb879fb 1345
5a09ae08
JF
1346 if (!version_.end())
1347 file_ = version_.FileList();
1348 else {
1349 pkgCache &cache([database_ cache]);
1350 file_ = pkgCache::VerFileIterator(cache, cache.VerFileP);
1351 }
b4d89997 1352
86316a91 1353 id_ = [[NSString stringWithUTF8String:iterator_.Name()] retain];
5a09ae08
JF
1354
1355 if (!file_.end()) {
1356 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
1357
1358 const char *begin, *end;
1359 parser->GetRec(begin, end);
1360
2083b866
JF
1361 NSString *website(nil);
1362 NSString *sponsor(nil);
1363 NSString *author(nil);
1364 NSString *tag(nil);
1365
1366 struct {
1367 const char *name_;
1368 NSString **value_;
1369 } names[] = {
1370 {"name", &name_},
1371 {"icon", &icon_},
1372 {"depiction", &depiction_},
1373 {"homepage", &homepage_},
1374 {"website", &website},
1375 {"sponsor", &sponsor},
1376 {"author", &author},
1377 {"tag", &tag},
1378 };
1379
1380 while (begin != end)
1381 if (*begin == '\n') {
1382 ++begin;
1383 continue;
1384 } else if (isblank(*begin)) next: {
1385 begin = static_cast<char *>(memchr(begin + 1, '\n', end - begin - 1));
1386 if (begin == NULL)
1387 break;
1388 } else if (const char *colon = static_cast<char *>(memchr(begin, ':', end - begin))) {
1389 const char *name(begin);
1390 size_t size(colon - begin);
1391
1392 begin = static_cast<char *>(memchr(begin, '\n', end - begin));
1393
1394 {
1395 const char *stop(begin == NULL ? end : begin);
1396 while (stop[-1] == '\r')
1397 --stop;
1398 while (++colon != stop && isblank(*colon));
1399
1400 for (size_t i(0); i != sizeof(names) / sizeof(names[0]); ++i)
1401 if (strncasecmp(names[i].name_, name, size) == 0) {
1402 NSString *value([NSString stringWithUTF8Bytes:colon length:(stop - colon)]);
1403 *names[i].value_ = value;
1404 break;
1405 }
1406 }
1407
1408 if (begin == NULL)
1409 break;
1410 ++begin;
1411 } else goto next;
1412
5a09ae08
JF
1413 if (name_ != nil)
1414 name_ = [name_ retain];
1415 tagline_ = [[NSString stringWithUTF8String:parser->ShortDesc().c_str()] retain];
5a09ae08
JF
1416 if (icon_ != nil)
1417 icon_ = [icon_ retain];
a9a0661e
JF
1418 if (depiction_ != nil)
1419 depiction_ = [depiction_ retain];
6f1a15d9 1420 if (homepage_ == nil)
2083b866 1421 homepage_ = website;
a9a0661e
JF
1422 if ([homepage_ isEqualToString:depiction_])
1423 homepage_ = nil;
6f1a15d9
JF
1424 if (homepage_ != nil)
1425 homepage_ = [homepage_ retain];
795d26fc
JF
1426 if (sponsor != nil)
1427 sponsor_ = [[Address addressWithString:sponsor] retain];
77fcccaf 1428 if (author != nil)
795d26fc 1429 author_ = [[Address addressWithString:author] retain];
2083b866
JF
1430 if (tag != nil)
1431 tags_ = [[tag componentsSeparatedByString:@", "] retain];
5a09ae08 1432 }
06aa974d 1433
7b0ce2da
JF
1434 if (tags_ != nil)
1435 for (int i(0), e([tags_ count]); i != e; ++i) {
1436 NSString *tag = [tags_ objectAtIndex:i];
1437 if ([tag hasPrefix:@"role::"]) {
1438 role_ = [[tag substringFromIndex:6] retain];
1439 break;
1440 }
1441 }
1442
a4b0ec52
JF
1443 NSString *solid(latest == nil ? installed : latest);
1444 bool changed(false);
1445
86316a91
JF
1446 NSString *key([id_ lowercaseString]);
1447
1448 NSMutableDictionary *metadata = [Packages_ objectForKey:key];
a4b0ec52 1449 if (metadata == nil) {
807ae6d7 1450 metadata = [[NSMutableDictionary dictionaryWithObjectsAndKeys:
36bb2ca2 1451 now_, @"FirstSeen",
807ae6d7 1452 nil] mutableCopy];
b4d89997 1453
a4b0ec52
JF
1454 if (solid != nil)
1455 [metadata setObject:solid forKey:@"LastVersion"];
1456 changed = true;
1457 } else {
1458 NSDate *first([metadata objectForKey:@"FirstSeen"]);
1459 NSDate *last([metadata objectForKey:@"LastSeen"]);
1460 NSString *version([metadata objectForKey:@"LastVersion"]);
1461
1462 if (first == nil) {
1463 first = last == nil ? now_ : last;
1464 [metadata setObject:first forKey:@"FirstSeen"];
1465 changed = true;
1466 }
1467
1468 if (solid != nil)
1469 if (version == nil) {
1470 [metadata setObject:solid forKey:@"LastVersion"];
1471 changed = true;
1472 } else if (![version isEqualToString:solid]) {
1473 [metadata setObject:solid forKey:@"LastVersion"];
1474 last = now_;
1475 [metadata setObject:last forKey:@"LastSeen"];
1476 changed = true;
1477 }
1478 }
1479
1480 if (changed) {
86316a91 1481 [Packages_ setObject:metadata forKey:key];
bbb879fb 1482 Changed_ = true;
36bb2ca2
JF
1483 }
1484 } return self;
dc5812ec
JF
1485}
1486
36bb2ca2 1487+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
5a09ae08
JF
1488 return [[[Package alloc]
1489 initWithIterator:iterator
1490 database:database
1491 ] autorelease];
dc5812ec
JF
1492}
1493
2388b078
JF
1494- (pkgCache::PkgIterator) iterator {
1495 return iterator_;
1496}
1497
36bb2ca2 1498- (NSString *) section {
807ae6d7
JF
1499 if (section_ != nil)
1500 return section_;
1501
36bb2ca2 1502 const char *section = iterator_.Section();
7b0ce2da
JF
1503 if (section == NULL)
1504 return nil;
46dbfd32
JF
1505
1506 NSString *name = [[NSString stringWithUTF8String:section] stringByReplacingCharacter:' ' withCharacter:'_'];
1507
1508 lookup:
1509 if (NSDictionary *value = [SectionMap_ objectForKey:name])
1510 if (NSString *rename = [value objectForKey:@"Rename"]) {
1511 name = rename;
1512 goto lookup;
1513 }
1514
807ae6d7
JF
1515 section_ = [[name stringByReplacingCharacter:'_' withCharacter:' '] retain];
1516 return section_;
dc5812ec
JF
1517}
1518
dec6029f
JF
1519- (NSString *) simpleSection {
1520 if (NSString *section = [self section])
1521 return Simplify(section);
1522 else
1523 return nil;
1524
1525}
1526
36bb2ca2 1527- (Address *) maintainer {
5a09ae08
JF
1528 if (file_.end())
1529 return nil;
36bb2ca2 1530 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
2388b078 1531 return [Address addressWithString:[NSString stringWithUTF8String:parser->Maintainer().c_str()]];
8fe19fc1
JF
1532}
1533
36bb2ca2 1534- (size_t) size {
5a09ae08 1535 return version_.end() ? 0 : version_->InstalledSize;
dc5812ec
JF
1536}
1537
36bb2ca2 1538- (NSString *) description {
5a09ae08
JF
1539 if (file_.end())
1540 return nil;
36bb2ca2 1541 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
2388b078 1542 NSString *description([NSString stringWithUTF8String:parser->LongDesc().c_str()]);
8fe19fc1 1543
36bb2ca2
JF
1544 NSArray *lines = [description componentsSeparatedByString:@"\n"];
1545 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
1546 if ([lines count] < 2)
1547 return nil;
3178d79b 1548
36bb2ca2
JF
1549 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
1550 for (size_t i(1); i != [lines count]; ++i) {
1551 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
1552 [trimmed addObject:trim];
1553 }
3178d79b 1554
36bb2ca2
JF
1555 return [trimmed componentsJoinedByString:@"\n"];
1556}
dc5812ec 1557
36bb2ca2
JF
1558- (NSString *) index {
1559 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1560 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1561}
3178d79b 1562
807ae6d7
JF
1563- (NSMutableDictionary *) metadata {
1564 return [Packages_ objectForKey:[id_ lowercaseString]];
1565}
1566
36bb2ca2 1567- (NSDate *) seen {
807ae6d7
JF
1568 NSDictionary *metadata([self metadata]);
1569 if ([self subscribed])
a4b0ec52
JF
1570 if (NSDate *last = [metadata objectForKey:@"LastSeen"])
1571 return last;
1572 return [metadata objectForKey:@"FirstSeen"];
3178d79b
JF
1573}
1574
807ae6d7
JF
1575- (BOOL) subscribed {
1576 NSDictionary *metadata([self metadata]);
1577 if (NSNumber *subscribed = [metadata objectForKey:@"IsSubscribed"])
1578 return [subscribed boolValue];
1579 else
1580 return false;
1581}
1582
1583- (BOOL) ignored {
1584 NSDictionary *metadata([self metadata]);
1585 if (NSNumber *ignored = [metadata objectForKey:@"IsIgnored"])
1586 return [ignored boolValue];
1587 else
1588 return false;
1589}
1590
36bb2ca2
JF
1591- (NSString *) latest {
1592 return latest_;
8fe19fc1
JF
1593}
1594
36bb2ca2
JF
1595- (NSString *) installed {
1596 return installed_;
3178d79b
JF
1597}
1598
5a09ae08
JF
1599- (BOOL) valid {
1600 return !version_.end();
1601}
1602
31f3cfff 1603- (BOOL) upgradableAndEssential:(BOOL)essential {
a54b1c10
JF
1604 pkgCache::VerIterator current = iterator_.CurrentVer();
1605
1606 if (current.end())
31f3cfff 1607 return essential && [self essential];
9487f027
JF
1608 else
1609 return !version_.end() && version_ != current;
36bb2ca2 1610}
3178d79b 1611
36bb2ca2
JF
1612- (BOOL) essential {
1613 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
3178d79b
JF
1614}
1615
36bb2ca2 1616- (BOOL) broken {
9bedffaa
JF
1617 return [database_ cache][iterator_].InstBroken();
1618}
1619
7d2ac47f 1620- (BOOL) unfiltered {
6d9712c4 1621 NSString *section = [self section];
7d2ac47f
JF
1622 return section == nil || isSectionVisible(section);
1623}
1624
1625- (BOOL) visible {
1626 return [self hasSupportingRole] && [self unfiltered];
6d9712c4
JF
1627}
1628
9bedffaa
JF
1629- (BOOL) half {
1630 unsigned char current = iterator_->CurrentState;
1631 return current == pkgCache::State::HalfConfigured || current == pkgCache::State::HalfInstalled;
1632}
1633
1634- (BOOL) halfConfigured {
1635 return iterator_->CurrentState == pkgCache::State::HalfConfigured;
1636}
1637
1638- (BOOL) halfInstalled {
1639 return iterator_->CurrentState == pkgCache::State::HalfInstalled;
1640}
1641
1642- (BOOL) hasMode {
1643 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1644 return state.Mode != pkgDepCache::ModeKeep;
1645}
1646
1647- (NSString *) mode {
1648 pkgDepCache::StateCache &state([database_ cache][iterator_]);
1649
1650 switch (state.Mode) {
1651 case pkgDepCache::ModeDelete:
1652 if ((state.iFlags & pkgDepCache::Purge) != 0)
1653 return @"Purge";
1654 else
1655 return @"Remove";
9bedffaa
JF
1656 case pkgDepCache::ModeKeep:
1657 if ((state.iFlags & pkgDepCache::AutoKept) != 0)
1658 return nil;
1659 else
1660 return nil;
9bedffaa
JF
1661 case pkgDepCache::ModeInstall:
1662 if ((state.iFlags & pkgDepCache::ReInstall) != 0)
1663 return @"Reinstall";
1664 else switch (state.Status) {
1665 case -1:
1666 return @"Downgrade";
1667 case 0:
1668 return @"Install";
1669 case 1:
1670 return @"Upgrade";
1671 case 2:
1672 return @"New Install";
1673 default:
1674 _assert(false);
1675 }
1676 default:
1677 _assert(false);
1678 }
8fe19fc1
JF
1679}
1680
36bb2ca2
JF
1681- (NSString *) id {
1682 return id_;
8fe19fc1
JF
1683}
1684
36bb2ca2
JF
1685- (NSString *) name {
1686 return name_ == nil ? id_ : name_;
1687}
8fe19fc1 1688
36bb2ca2
JF
1689- (NSString *) tagline {
1690 return tagline_;
8fe19fc1
JF
1691}
1692
770f2a8e 1693- (UIImage *) icon {
dec6029f 1694 NSString *section = [self simpleSection];
770f2a8e
JF
1695
1696 UIImage *icon(nil);
1697 if (NSString *icon = icon_)
1698 icon = [UIImage imageAtPath:[icon_ substringFromIndex:6]];
1699 if (icon == nil) if (section != nil)
1700 icon = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]];
1701 if (icon == nil) if (source_ != nil) if (NSString *icon = [source_ defaultIcon])
1702 icon = [UIImage imageAtPath:[icon substringFromIndex:6]];
1703 if (icon == nil)
1704 icon = [UIImage applicationImageNamed:@"unknown.png"];
1705 return icon;
36bb2ca2 1706}
8fe19fc1 1707
6f1a15d9 1708- (NSString *) homepage {
3a49daf0 1709 return homepage_;
8fe19fc1
JF
1710}
1711
25a2158d
JF
1712- (NSString *) depiction {
1713 return depiction_;
1714}
1715
795d26fc
JF
1716- (Address *) sponsor {
1717 return sponsor_;
1718}
1719
77fcccaf
JF
1720- (Address *) author {
1721 return author_;
1722}
1723
affeffc7
JF
1724- (NSArray *) files {
1725 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", id_];
1726 NSMutableArray *files = [NSMutableArray arrayWithCapacity:128];
1727
1728 std::ifstream fin;
1729 fin.open([path UTF8String]);
1730 if (!fin.is_open())
1731 return nil;
1732
1733 std::string line;
1734 while (std::getline(fin, line))
1735 [files addObject:[NSString stringWithUTF8String:line.c_str()]];
1736
1737 return files;
1738}
1739
2388b078
JF
1740- (NSArray *) relationships {
1741 return relationships_;
1742}
1743
affeffc7
JF
1744- (NSArray *) warnings {
1745 NSMutableArray *warnings([NSMutableArray arrayWithCapacity:4]);
1746 const char *name(iterator_.Name());
1747
1748 size_t length(strlen(name));
1749 if (length < 2) invalid:
1750 [warnings addObject:@"illegal package identifier"];
1751 else for (size_t i(0); i != length; ++i)
1752 if (
1753 (name[i] < 'a' || name[i] > 'z') &&
1754 (name[i] < '0' || name[i] > '9') &&
1755 (i == 0 || name[i] != '+' && name[i] != '-' && name[i] != '.')
1756 ) goto invalid;
1757
1758 if (strcmp(name, "cydia") != 0) {
1759 bool cydia = false;
1760 bool stash = false;
1761
1762 if (NSArray *files = [self files])
1763 for (NSString *file in files)
1764 if (!cydia && [file isEqualToString:@"/Applications/Cydia.app"])
1765 cydia = true;
1766 else if (!stash && [file isEqualToString:@"/var/stash"])
1767 stash = true;
1768
1769 if (cydia)
1770 [warnings addObject:@"files installed into Cydia.app"];
1771 if (stash)
1772 [warnings addObject:@"files installed to /var/stash"];
1773 }
1774
1775 return [warnings count] == 0 ? nil : warnings;
1776}
1777
1778- (NSArray *) applications {
1779 NSString *me([[NSBundle mainBundle] bundleIdentifier]);
1780
1781 NSMutableArray *applications([NSMutableArray arrayWithCapacity:2]);
1782
1783 static Pcre application_r("^/Applications/(.*)\\.app/Info.plist$");
1784 if (NSArray *files = [self files])
1785 for (NSString *file in files)
1786 if (application_r(file)) {
1787 NSDictionary *info([NSDictionary dictionaryWithContentsOfFile:file]);
1788 NSString *id([info objectForKey:@"CFBundleIdentifier"]);
1789 if ([id isEqualToString:me])
1790 continue;
1791
1792 NSString *display([info objectForKey:@"CFBundleDisplayName"]);
1793 if (display == nil)
1794 display = application_r[1];
1795
1796 NSString *bundle([file stringByDeletingLastPathComponent]);
1797 NSString *icon([info objectForKey:@"CFBundleIconFile"]);
1798 if (icon == nil || [icon length] == 0)
1799 icon = @"icon.png";
1800 NSURL *url([NSURL fileURLWithPath:[bundle stringByAppendingPathComponent:icon]]);
1801
1802 NSMutableArray *application([NSMutableArray arrayWithCapacity:2]);
1803 [applications addObject:application];
1804
1805 [application addObject:id];
1806 [application addObject:display];
1807 [application addObject:url];
1808 }
1809
1810 return [applications count] == 0 ? nil : applications;
1811}
1812
36bb2ca2 1813- (Source *) source {
bbb879fb 1814 if (!cached_) {
5a09ae08 1815 source_ = file_.end() ? nil : [[database_ getSource:file_.File()] retain];
bbb879fb
JF
1816 cached_ = true;
1817 }
1818
36bb2ca2 1819 return source_;
8fe19fc1
JF
1820}
1821
7b0ce2da
JF
1822- (NSString *) role {
1823 return role_;
1824}
1825
680eb135 1826- (NSString *) rating {
6565453a
JF
1827 if (NSString *rating = [Indices_ objectForKey:@"Rating"])
1828 return [rating stringByReplacingOccurrencesOfString:@"@P" withString:[id_ stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1829 else
680eb135
JF
1830 return nil;
1831}
1832
36bb2ca2
JF
1833- (BOOL) matches:(NSString *)text {
1834 if (text == nil)
1835 return NO;
8fe19fc1 1836
36bb2ca2 1837 NSRange range;
8fe19fc1 1838
baf80942 1839 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
36bb2ca2
JF
1840 if (range.location != NSNotFound)
1841 return YES;
8fe19fc1 1842
36bb2ca2
JF
1843 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1844 if (range.location != NSNotFound)
1845 return YES;
8fe19fc1 1846
36bb2ca2
JF
1847 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1848 if (range.location != NSNotFound)
1849 return YES;
8fe19fc1 1850
36bb2ca2
JF
1851 return NO;
1852}
8fe19fc1 1853
7b0ce2da
JF
1854- (bool) hasSupportingRole {
1855 if (role_ == nil)
6d9712c4 1856 return true;
7b0ce2da
JF
1857 if ([role_ isEqualToString:@"enduser"])
1858 return true;
1859 if ([Role_ isEqualToString:@"User"])
1860 return false;
1861 if ([role_ isEqualToString:@"hacker"])
1862 return true;
1863 if ([Role_ isEqualToString:@"Hacker"])
1864 return false;
1865 if ([role_ isEqualToString:@"developer"])
1866 return true;
1867 if ([Role_ isEqualToString:@"Developer"])
1868 return false;
1869 _assert(false);
6d9712c4
JF
1870}
1871
1872- (BOOL) hasTag:(NSString *)tag {
1873 return tags_ == nil ? NO : [tags_ containsObject:tag];
1874}
1875
c390d3ab
JF
1876- (NSString *) primaryPurpose {
1877 for (NSString *tag in tags_)
1878 if ([tag hasPrefix:@"purpose::"])
1879 return [tag substringFromIndex:9];
1880 return nil;
1881}
1882
770f2a8e
JF
1883- (NSArray *) purposes {
1884 NSMutableArray *purposes([NSMutableArray arrayWithCapacity:2]);
1885 for (NSString *tag in tags_)
1886 if ([tag hasPrefix:@"purpose::"])
1887 [purposes addObject:[tag substringFromIndex:9]];
1888 return [purposes count] == 0 ? nil : purposes;
1889}
1890
36bb2ca2
JF
1891- (NSComparisonResult) compareByName:(Package *)package {
1892 NSString *lhs = [self name];
1893 NSString *rhs = [package name];
8fe19fc1 1894
36bb2ca2
JF
1895 if ([lhs length] != 0 && [rhs length] != 0) {
1896 unichar lhc = [lhs characterAtIndex:0];
1897 unichar rhc = [rhs characterAtIndex:0];
1898
1899 if (isalpha(lhc) && !isalpha(rhc))
1900 return NSOrderedAscending;
1901 else if (!isalpha(lhc) && isalpha(rhc))
1902 return NSOrderedDescending;
8fe19fc1
JF
1903 }
1904
68f1828e 1905 return [lhs compare:rhs options:LaxCompareOptions_];
8fe19fc1
JF
1906}
1907
36bb2ca2
JF
1908- (NSComparisonResult) compareBySection:(Package *)package {
1909 NSString *lhs = [self section];
1910 NSString *rhs = [package section];
3178d79b 1911
36bb2ca2
JF
1912 if (lhs == NULL && rhs != NULL)
1913 return NSOrderedAscending;
1914 else if (lhs != NULL && rhs == NULL)
1915 return NSOrderedDescending;
1916 else if (lhs != NULL && rhs != NULL) {
68f1828e
JF
1917 NSComparisonResult result([lhs compare:rhs options:LaxCompareOptions_]);
1918 return result != NSOrderedSame ? result : [lhs compare:rhs options:ForcedCompareOptions_];
36bb2ca2 1919 }
3178d79b 1920
36bb2ca2
JF
1921 return NSOrderedSame;
1922}
3178d79b 1923
807ae6d7
JF
1924- (uint32_t) compareForChanges {
1925 union {
1926 uint32_t key;
3178d79b 1927
807ae6d7
JF
1928 struct {
1929 uint32_t timestamp : 30;
1930 uint32_t ignored : 1;
1931 uint32_t upgradable : 1;
1932 } bits;
1933 } value;
3178d79b 1934
807ae6d7 1935 value.bits.upgradable = [self upgradableAndEssential:YES] ? 1 : 0;
8fe19fc1 1936
807ae6d7
JF
1937 if ([self upgradableAndEssential:YES]) {
1938 value.bits.timestamp = 0;
1939 value.bits.ignored = [self ignored] ? 0 : 1;
1940 value.bits.upgradable = 1;
1941 } else {
1942 value.bits.timestamp = static_cast<uint32_t>([[self seen] timeIntervalSince1970]) >> 2;
1943 value.bits.ignored = 0;
1944 value.bits.upgradable = 0;
36bb2ca2 1945 }
8fe19fc1 1946
807ae6d7 1947 return _not(uint32_t) - value.key;
36bb2ca2 1948}
8fe19fc1 1949
36bb2ca2
JF
1950- (void) install {
1951 pkgProblemResolver *resolver = [database_ resolver];
1952 resolver->Clear(iterator_);
1953 resolver->Protect(iterator_);
1954 pkgCacheFile &cache([database_ cache]);
1955 cache->MarkInstall(iterator_, false);
1956 pkgDepCache::StateCache &state((*cache)[iterator_]);
1957 if (!state.Install())
1958 cache->SetReInstall(iterator_, true);
1959}
68a238ec 1960
36bb2ca2
JF
1961- (void) remove {
1962 pkgProblemResolver *resolver = [database_ resolver];
1963 resolver->Clear(iterator_);
1964 resolver->Protect(iterator_);
1965 resolver->Remove(iterator_);
1966 [database_ cache]->MarkDelete(iterator_, true);
1967}
8fe19fc1 1968
7d2ac47f 1969- (NSNumber *) isUnfilteredAndSearchedForBy:(NSString *)search {
f641a0e5 1970 return [NSNumber numberWithBool:(
7d2ac47f 1971 [self unfiltered] && [self matches:search]
f641a0e5 1972 )];
36bb2ca2 1973}
8fe19fc1 1974
440c215d 1975- (NSNumber *) isInstalledAndVisible:(NSNumber *)number {
f641a0e5 1976 return [NSNumber numberWithBool:(
440c215d 1977 (![number boolValue] || [self visible]) && [self installed] != nil
f641a0e5 1978 )];
36bb2ca2 1979}
8fe19fc1 1980
7b0ce2da 1981- (NSNumber *) isVisiblyUninstalledInSection:(NSString *)name {
5a09ae08
JF
1982 NSString *section = [self section];
1983
7b0ce2da 1984 return [NSNumber numberWithBool:(
7d2ac47f 1985 [self visible] &&
7b0ce2da
JF
1986 [self installed] == nil && (
1987 name == nil ||
1988 section == nil && [name length] == 0 ||
1989 [name isEqualToString:section]
1990 )
1991 )];
1992}
1993
1994- (NSNumber *) isVisibleInSource:(Source *)source {
1995 return [NSNumber numberWithBool:([self source] == source && [self visible])];
36bb2ca2 1996}
8fe19fc1 1997
36bb2ca2
JF
1998@end
1999/* }}} */
2000/* Section Class {{{ */
2001@interface Section : NSObject {
2002 NSString *name_;
2003 size_t row_;
2004 size_t count_;
2005}
3178d79b 2006
6d9712c4
JF
2007- (NSComparisonResult) compareByName:(Section *)section;
2008- (Section *) initWithName:(NSString *)name;
36bb2ca2
JF
2009- (Section *) initWithName:(NSString *)name row:(size_t)row;
2010- (NSString *) name;
2011- (size_t) row;
2012- (size_t) count;
2013- (void) addToCount;
b19871dd 2014
36bb2ca2 2015@end
b19871dd 2016
36bb2ca2 2017@implementation Section
b19871dd 2018
36bb2ca2
JF
2019- (void) dealloc {
2020 [name_ release];
2021 [super dealloc];
2022}
b19871dd 2023
6d9712c4
JF
2024- (NSComparisonResult) compareByName:(Section *)section {
2025 NSString *lhs = [self name];
2026 NSString *rhs = [section name];
2027
2028 if ([lhs length] != 0 && [rhs length] != 0) {
2029 unichar lhc = [lhs characterAtIndex:0];
2030 unichar rhc = [rhs characterAtIndex:0];
2031
2032 if (isalpha(lhc) && !isalpha(rhc))
2033 return NSOrderedAscending;
2034 else if (!isalpha(lhc) && isalpha(rhc))
2035 return NSOrderedDescending;
2036 }
2037
68f1828e 2038 return [lhs compare:rhs options:LaxCompareOptions_];
6d9712c4
JF
2039}
2040
2041- (Section *) initWithName:(NSString *)name {
2042 return [self initWithName:name row:0];
2043}
2044
36bb2ca2
JF
2045- (Section *) initWithName:(NSString *)name row:(size_t)row {
2046 if ((self = [super init]) != nil) {
2047 name_ = [name retain];
2048 row_ = row;
b19871dd
JF
2049 } return self;
2050}
2051
36bb2ca2
JF
2052- (NSString *) name {
2053 return name_;
2054}
2055
2056- (size_t) row {
2057 return row_;
2058}
2059
2060- (size_t) count {
2061 return count_;
2062}
2063
2064- (void) addToCount {
2065 ++count_;
2066}
2067
b19871dd
JF
2068@end
2069/* }}} */
2070
affeffc7
JF
2071static int Finish_;
2072static NSArray *Finishes_;
bc8cd583 2073
36bb2ca2
JF
2074/* Database Implementation {{{ */
2075@implementation Database
ec97ef06 2076
770f2a8e
JF
2077+ (Database *) sharedInstance {
2078 static Database *instance;
2079 if (instance == nil)
2080 instance = [[Database alloc] init];
2081 return instance;
2082}
2083
36bb2ca2
JF
2084- (void) dealloc {
2085 _assert(false);
2086 [super dealloc];
2087}
ec97ef06 2088
affeffc7 2089- (void) _readCydia:(NSNumber *)fd { _pooled
77fcccaf
JF
2090 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2091 std::istream is(&ib);
2092 std::string line;
2093
bc8cd583
JF
2094 static Pcre finish_r("^finish:([^:]*)$");
2095
77fcccaf
JF
2096 while (std::getline(is, line)) {
2097 const char *data(line.c_str());
bc8cd583 2098 size_t size = line.size();
c390d3ab 2099 lprintf("C:%s\n", data);
bc8cd583
JF
2100
2101 if (finish_r(data, size)) {
2102 NSString *finish = finish_r[1];
2103 int index = [Finishes_ indexOfObject:finish];
2104 if (index != INT_MAX && index > Finish_)
2105 Finish_ = index;
2106 }
77fcccaf
JF
2107 }
2108
77fcccaf
JF
2109 _assert(false);
2110}
2111
affeffc7 2112- (void) _readStatus:(NSNumber *)fd { _pooled
36bb2ca2
JF
2113 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2114 std::istream is(&ib);
2115 std::string line;
ec97ef06 2116
7b0ce2da
JF
2117 static Pcre conffile_r("^status: [^ ]* : conffile-prompt : (.*?) *$");
2118 static Pcre pmstatus_r("^([^:]*):([^:]*):([^:]*):(.*)$");
ec97ef06 2119
36bb2ca2
JF
2120 while (std::getline(is, line)) {
2121 const char *data(line.c_str());
5a09ae08 2122 size_t size = line.size();
c390d3ab 2123 lprintf("S:%s\n", data);
ec97ef06 2124
5a09ae08
JF
2125 if (conffile_r(data, size)) {
2126 [delegate_ setConfigurationData:conffile_r[1]];
2127 } else if (strncmp(data, "status: ", 8) == 0) {
2128 NSString *string = [NSString stringWithUTF8String:(data + 8)];
36bb2ca2 2129 [delegate_ setProgressTitle:string];
5a09ae08 2130 } else if (pmstatus_r(data, size)) {
31f3cfff
JF
2131 std::string type([pmstatus_r[1] UTF8String]);
2132 NSString *id = pmstatus_r[2];
2133
5a09ae08
JF
2134 float percent([pmstatus_r[3] floatValue]);
2135 [delegate_ setProgressPercent:(percent / 100)];
2136
2137 NSString *string = pmstatus_r[4];
5a09ae08
JF
2138
2139 if (type == "pmerror")
31f3cfff
JF
2140 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2141 withObject:[NSArray arrayWithObjects:string, id, nil]
2142 waitUntilDone:YES
2143 ];
9487f027 2144 else if (type == "pmstatus") {
5a09ae08 2145 [delegate_ setProgressTitle:string];
9487f027 2146 } else if (type == "pmconffile")
5a09ae08
JF
2147 [delegate_ setConfigurationData:string];
2148 else _assert(false);
2149 } else _assert(false);
36bb2ca2 2150 }
ec97ef06 2151
36bb2ca2
JF
2152 _assert(false);
2153}
ec97ef06 2154
affeffc7 2155- (void) _readOutput:(NSNumber *)fd { _pooled
36bb2ca2
JF
2156 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
2157 std::istream is(&ib);
2158 std::string line;
2159
5a09ae08 2160 while (std::getline(is, line)) {
c390d3ab 2161 lprintf("O:%s\n", line.c_str());
2388b078 2162 [delegate_ addProgressOutput:[NSString stringWithUTF8String:line.c_str()]];
5a09ae08 2163 }
36bb2ca2 2164
36bb2ca2 2165 _assert(false);
ec97ef06
JF
2166}
2167
8b29f8e6
JF
2168- (FILE *) input {
2169 return input_;
2170}
2171
36bb2ca2 2172- (Package *) packageWithName:(NSString *)name {
5a09ae08
JF
2173 if (static_cast<pkgDepCache *>(cache_) == NULL)
2174 return nil;
36bb2ca2
JF
2175 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
2176 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
2177}
2178
2179- (Database *) init {
ec97ef06 2180 if ((self = [super init]) != nil) {
5a09ae08 2181 policy_ = NULL;
36bb2ca2
JF
2182 records_ = NULL;
2183 resolver_ = NULL;
2184 fetcher_ = NULL;
2185 lock_ = NULL;
ec97ef06 2186
36bb2ca2
JF
2187 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
2188 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
ec97ef06 2189
36bb2ca2 2190 int fds[2];
ec97ef06 2191
77fcccaf
JF
2192 _assert(pipe(fds) != -1);
2193 cydiafd_ = fds[1];
2194
2195 _config->Set("APT::Keep-Fds::", cydiafd_);
bc8cd583 2196 setenv("CYDIA", [[[[NSNumber numberWithInt:cydiafd_] stringValue] stringByAppendingString:@" 1"] UTF8String], _not(int));
77fcccaf
JF
2197
2198 [NSThread
2199 detachNewThreadSelector:@selector(_readCydia:)
2200 toTarget:self
2201 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2202 ];
2203
36bb2ca2
JF
2204 _assert(pipe(fds) != -1);
2205 statusfd_ = fds[1];
ec97ef06 2206
36bb2ca2
JF
2207 [NSThread
2208 detachNewThreadSelector:@selector(_readStatus:)
2209 toTarget:self
2210 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2211 ];
ec97ef06 2212
8b29f8e6
JF
2213 _assert(pipe(fds) != -1);
2214 _assert(dup2(fds[0], 0) != -1);
2215 _assert(close(fds[0]) != -1);
2216
2217 input_ = fdopen(fds[1], "a");
2218
36bb2ca2
JF
2219 _assert(pipe(fds) != -1);
2220 _assert(dup2(fds[1], 1) != -1);
2221 _assert(close(fds[1]) != -1);
2222
2223 [NSThread
2224 detachNewThreadSelector:@selector(_readOutput:)
2225 toTarget:self
2226 withObject:[[NSNumber numberWithInt:fds[0]] retain]
2227 ];
ec97ef06
JF
2228 } return self;
2229}
2230
36bb2ca2
JF
2231- (pkgCacheFile &) cache {
2232 return cache_;
ec97ef06
JF
2233}
2234
5a09ae08
JF
2235- (pkgDepCache::Policy *) policy {
2236 return policy_;
2237}
2238
36bb2ca2
JF
2239- (pkgRecords *) records {
2240 return records_;
ec97ef06
JF
2241}
2242
36bb2ca2
JF
2243- (pkgProblemResolver *) resolver {
2244 return resolver_;
ec97ef06
JF
2245}
2246
36bb2ca2
JF
2247- (pkgAcquire &) fetcher {
2248 return *fetcher_;
ec97ef06
JF
2249}
2250
36bb2ca2
JF
2251- (NSArray *) packages {
2252 return packages_;
ec97ef06
JF
2253}
2254
7b0ce2da
JF
2255- (NSArray *) sources {
2256 return [sources_ allValues];
2257}
2258
affeffc7
JF
2259- (NSArray *) issues {
2260 if (cache_->BrokenCount() == 0)
2261 return nil;
2262
2263 NSMutableArray *issues([NSMutableArray arrayWithCapacity:4]);
2264
2265 for (Package *package in packages_) {
2266 if (![package broken])
2267 continue;
2268 pkgCache::PkgIterator pkg([package iterator]);
2269
2270 NSMutableArray *entry([NSMutableArray arrayWithCapacity:4]);
2271 [entry addObject:[package name]];
2272 [issues addObject:entry];
2273
2274 pkgCache::VerIterator ver(cache_[pkg].InstVerIter(cache_));
2275 if (ver.end())
2276 continue;
2277
2278 for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
2279 pkgCache::DepIterator start;
2280 pkgCache::DepIterator end;
2281 dep.GlobOr(start, end); // ++dep
2282
2283 if (!cache_->IsImportantDep(end))
2284 continue;
2285 if ((cache_[end] & pkgDepCache::DepGInstall) != 0)
2286 continue;
2287
2288 NSMutableArray *failure([NSMutableArray arrayWithCapacity:4]);
2289 [entry addObject:failure];
2290 [failure addObject:[NSString stringWithUTF8String:start.DepType()]];
2291
2292 Package *package([self packageWithName:[NSString stringWithUTF8String:start.TargetPkg().Name()]]);
2293 [failure addObject:[package name]];
2294
2295 pkgCache::PkgIterator target(start.TargetPkg());
2296 if (target->ProvidesList != 0)
2297 [failure addObject:@"?"];
2298 else {
2299 pkgCache::VerIterator ver(cache_[target].InstVerIter(cache_));
2300 if (!ver.end())
2301 [failure addObject:[NSString stringWithUTF8String:ver.VerStr()]];
2302 else if (!cache_[target].CandidateVerIter(cache_).end())
2303 [failure addObject:@"-"];
2304 else if (target->ProvidesList == 0)
2305 [failure addObject:@"!"];
2306 else
2307 [failure addObject:@"%"];
2308 }
2309
2310 _forever {
2311 if (start.TargetVer() != 0)
2312 [failure addObject:[NSString stringWithFormat:@"%s %s", start.CompType(), start.TargetVer()]];
2313 if (start == end)
2314 break;
2315 ++start;
2316 }
2317 }
2318 }
2319
2320 return issues;
2321}
2322
36bb2ca2
JF
2323- (void) reloadData {
2324 _error->Discard();
5a09ae08 2325
36bb2ca2 2326 delete list_;
5a09ae08 2327 list_ = NULL;
36bb2ca2
JF
2328 manager_ = NULL;
2329 delete lock_;
5a09ae08 2330 lock_ = NULL;
36bb2ca2 2331 delete fetcher_;
5a09ae08 2332 fetcher_ = NULL;
36bb2ca2 2333 delete resolver_;
5a09ae08 2334 resolver_ = NULL;
36bb2ca2 2335 delete records_;
5a09ae08
JF
2336 records_ = NULL;
2337 delete policy_;
2338 policy_ = NULL;
36bb2ca2 2339
5a09ae08 2340 cache_.Close();
8b29f8e6 2341
9487f027 2342 _trace();
5a09ae08 2343 if (!cache_.Open(progress_, true)) {
8b29f8e6
JF
2344 std::string error;
2345 if (!_error->PopMessage(error))
2346 _assert(false);
5a09ae08 2347 _error->Discard();
c390d3ab 2348 lprintf("cache_.Open():[%s]\n", error.c_str());
5a09ae08
JF
2349
2350 if (error == "dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem. ")
2351 [delegate_ repairWithSelector:@selector(configure)];
2352 else if (error == "The package lists or status file could not be parsed or opened.")
2353 [delegate_ repairWithSelector:@selector(update)];
9ea8d159
JF
2354 // else if (error == "Could not open lock file /var/lib/dpkg/lock - open (13 Permission denied)")
2355 // else if (error == "Could not get lock /var/lib/dpkg/lock - open (35 Resource temporarily unavailable)")
2356 // else if (error == "The list of sources could not be read.")
5a09ae08
JF
2357 else _assert(false);
2358
2359 return;
36bb2ca2 2360 }
9487f027 2361 _trace();
36bb2ca2
JF
2362
2363 now_ = [[NSDate date] retain];
2364
5a09ae08 2365 policy_ = new pkgDepCache::Policy();
36bb2ca2
JF
2366 records_ = new pkgRecords(cache_);
2367 resolver_ = new pkgProblemResolver(cache_);
2368 fetcher_ = new pkgAcquire(&status_);
2369 lock_ = NULL;
2370
2371 list_ = new pkgSourceList();
2372 _assert(list_->ReadMainList());
2373
9bedffaa
JF
2374 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
2375 _assert(pkgApplyStatus(cache_));
2376
2377 if (cache_->BrokenCount() != 0) {
2378 _assert(pkgFixBroken(cache_));
2379 _assert(cache_->BrokenCount() == 0);
2380 _assert(pkgMinimizeUpgrade(cache_));
2381 }
2382
36bb2ca2
JF
2383 [sources_ removeAllObjects];
2384 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
2385 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
2386 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
2387 [sources_
2388 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
2389 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
2390 ];
2391 }
2392
2393 [packages_ removeAllObjects];
9487f027 2394 _trace();
2083b866 2395 profile_ = 0;
36bb2ca2 2396 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
5a09ae08
JF
2397 if (Package *package = [Package packageWithIterator:iterator database:self])
2398 [packages_ addObject:package];
9487f027 2399 _trace();
ab398adf 2400 [packages_ sortUsingSelector:@selector(compareByName:)];
9487f027 2401 _trace();
ec97ef06
JF
2402}
2403
5a09ae08
JF
2404- (void) configure {
2405 NSString *dpkg = [NSString stringWithFormat:@"dpkg --configure -a --status-fd %u", statusfd_];
2406 system([dpkg UTF8String]);
2407}
2408
77fcccaf
JF
2409- (void) clean {
2410 if (lock_ != NULL)
2411 return;
2412
2413 FileFd Lock;
2414 Lock.Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2415 _assert(!_error->PendingError());
2416
2417 pkgAcquire fetcher;
2418 fetcher.Clean(_config->FindDir("Dir::Cache::Archives"));
2419
9bedffaa
JF
2420 class LogCleaner :
2421 public pkgArchiveCleaner
2422 {
77fcccaf
JF
2423 protected:
2424 virtual void Erase(const char *File, std::string Pkg, std::string Ver, struct stat &St) {
9bedffaa 2425 unlink(File);
77fcccaf
JF
2426 }
2427 } cleaner;
2428
2429 if (!cleaner.Go(_config->FindDir("Dir::Cache::Archives") + "partial/", cache_)) {
2430 std::string error;
2431 while (_error->PopMessage(error))
c390d3ab 2432 lprintf("ArchiveCleaner: %s\n", error.c_str());
77fcccaf
JF
2433 }
2434}
2435
36bb2ca2
JF
2436- (void) prepare {
2437 pkgRecords records(cache_);
2438
2439 lock_ = new FileFd();
2440 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
2441 _assert(!_error->PendingError());
2442
2443 pkgSourceList list;
2444 // XXX: explain this with an error message
2445 _assert(list.ReadMainList());
2446
2447 manager_ = (_system->CreatePM(cache_));
2448 _assert(manager_->GetArchives(fetcher_, &list, &records));
2449 _assert(!_error->PendingError());
ec97ef06
JF
2450}
2451
36bb2ca2 2452- (void) perform {
a72074b2
JF
2453 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
2454 pkgSourceList list;
2455 _assert(list.ReadMainList());
2456 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2457 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2458 }
a0be02eb 2459
eeb9b112
JF
2460 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue) {
2461 _trace();
36bb2ca2 2462 return;
eeb9b112
JF
2463 }
2464
2465 bool failed = false;
2466 for (pkgAcquire::ItemIterator item = fetcher_->ItemsBegin(); item != fetcher_->ItemsEnd(); item++) {
2467 if ((*item)->Status == pkgAcquire::Item::StatDone && (*item)->Complete)
2468 continue;
2469
2470 std::string uri = (*item)->DescURI();
2471 std::string error = (*item)->ErrorText;
2472
c390d3ab 2473 lprintf("pAf:%s:%s\n", uri.c_str(), error.c_str());
eeb9b112
JF
2474 failed = true;
2475
2476 [delegate_ performSelectorOnMainThread:@selector(_setProgressError:)
2477 withObject:[NSArray arrayWithObjects:[NSString stringWithUTF8String:error.c_str()], nil]
2478 waitUntilDone:YES
2479 ];
2480 }
2481
2482 if (failed) {
2483 _trace();
2484 return;
2485 }
36bb2ca2
JF
2486
2487 _system->UnLock();
2488 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
2489
eeb9b112
JF
2490 if (_error->PendingError()) {
2491 _trace();
36bb2ca2 2492 return;
eeb9b112
JF
2493 }
2494
2495 if (result == pkgPackageManager::Failed) {
2496 _trace();
36bb2ca2 2497 return;
eeb9b112
JF
2498 }
2499
2500 if (result != pkgPackageManager::Completed) {
2501 _trace();
36bb2ca2 2502 return;
eeb9b112 2503 }
a0be02eb 2504
a72074b2
JF
2505 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
2506 pkgSourceList list;
2507 _assert(list.ReadMainList());
2508 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
2509 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
2510 }
2511
2512 if (![before isEqualToArray:after])
2513 [self update];
ec97ef06
JF
2514}
2515
36bb2ca2 2516- (void) upgrade {
36bb2ca2 2517 _assert(pkgDistUpgrade(cache_));
c7c6384e
JF
2518}
2519
36bb2ca2
JF
2520- (void) update {
2521 [self updateWithStatus:status_];
2522}
b4d89997 2523
36bb2ca2
JF
2524- (void) updateWithStatus:(Status &)status {
2525 pkgSourceList list;
2526 _assert(list.ReadMainList());
b4d89997 2527
36bb2ca2
JF
2528 FileFd lock;
2529 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
2530 _assert(!_error->PendingError());
2531
2532 pkgAcquire fetcher(&status);
2533 _assert(list.GetIndexes(&fetcher));
2534
2535 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
2536 bool failed = false;
2537 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
2538 if ((*item)->Status != pkgAcquire::Item::StatDone) {
2539 (*item)->Finished();
2540 failed = true;
2541 }
2542
2543 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
2544 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
2545 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
b4d89997 2546 }
36bb2ca2
JF
2547
2548 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
bbb879fb 2549 Changed_ = true;
b4d89997
JF
2550 }
2551}
2552
36bb2ca2
JF
2553- (void) setDelegate:(id)delegate {
2554 delegate_ = delegate;
2555 status_.setDelegate(delegate);
2556 progress_.setDelegate(delegate);
2557}
b4d89997 2558
36bb2ca2
JF
2559- (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
2560 pkgIndexFile *index(NULL);
2561 list_->FindIndex(file, index);
2562 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
b19871dd
JF
2563}
2564
36bb2ca2
JF
2565@end
2566/* }}} */
b4d89997 2567
affeffc7 2568/* PopUp Windows {{{ */
c390d3ab
JF
2569@interface PopUpView : UIView {
2570 _transient id delegate_;
2571 UITransitionView *transition_;
2572 UIView *overlay_;
2573}
2574
2575- (void) cancel;
2576- (id) initWithView:(UIView *)view delegate:(id)delegate;
2577
2578@end
2579
2580@implementation PopUpView
2581
2582- (void) dealloc {
2583 [transition_ setDelegate:nil];
2584 [transition_ release];
2585 [overlay_ release];
2586 [super dealloc];
2587}
2588
2589- (void) cancel {
2590 [transition_ transition:UITransitionPushFromTop toView:nil];
2591}
2592
2593- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2594 if (from != nil && to == nil)
2595 [self removeFromSuperview];
2596}
2597
2598- (id) initWithView:(UIView *)view delegate:(id)delegate {
2599 if ((self = [super initWithFrame:[view bounds]]) != nil) {
2600 delegate_ = delegate;
2601
2602 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2603 [self addSubview:transition_];
2604
2605 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
2606
2607 [view addSubview:self];
2608
2609 [transition_ setDelegate:self];
2610
2611 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2612 [transition_ transition:UITransitionNone toView:blank];
2613 [transition_ transition:UITransitionPushFromBottom toView:overlay_];
2614 } return self;
2615}
2616
2617@end
2618/* }}} */
2619
2620/* Mail Composition {{{ */
2621@interface MailToView : PopUpView {
2622 MailComposeController *controller_;
2623}
2624
2625- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url;
2626
2627@end
2628
2629@implementation MailToView
2630
2631- (void) dealloc {
2632 [controller_ release];
2633 [super dealloc];
2634}
2635
c390d3ab
JF
2636- (void) mailComposeControllerWillAttemptToSend:(MailComposeController *)controller {
2637 NSLog(@"will");
2638}
2639
2640- (void) mailComposeControllerDidAttemptToSend:(MailComposeController *)controller mailDelivery:(id)delivery {
2641 NSLog(@"did:%@", delivery);
2642// [UIApp setStatusBarShowsProgress:NO];
2643if ([controller error]){
2644NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2645UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2646[mailAlertSheet setBodyText:[controller error]];
2647[mailAlertSheet popupAlertAnimated:YES];
2648}
2649}
2650
affeffc7
JF
2651- (void) showError {
2652 NSLog(@"%@", [controller_ error]);
2653 NSArray *buttons = [NSArray arrayWithObjects:@"OK", nil];
2654 UIActionSheet *mailAlertSheet = [[UIActionSheet alloc] initWithTitle:@"Error" buttons:buttons defaultButtonIndex:0 delegate:self context:self];
2655 [mailAlertSheet setBodyText:[controller_ error]];
2656 [mailAlertSheet popupAlertAnimated:YES];
2657}
2658
2659- (void) deliverMessage { _pooled
86316a91
JF
2660 setuid(501);
2661 setgid(501);
2662
affeffc7
JF
2663 if (![controller_ deliverMessage])
2664 [self performSelectorOnMainThread:@selector(showError) withObject:nil waitUntilDone:NO];
2665}
2666
c390d3ab 2667- (void) mailComposeControllerCompositionFinished:(MailComposeController *)controller {
affeffc7
JF
2668 if ([controller_ needsDelivery])
2669 [NSThread detachNewThreadSelector:@selector(deliverMessage) toTarget:self withObject:nil];
c390d3ab
JF
2670 else
2671 [self cancel];
2672}
2673
2674- (id) initWithView:(UIView *)view delegate:(id)delegate url:(NSURL *)url {
2675 if ((self = [super initWithView:view delegate:delegate]) != nil) {
2676 controller_ = [[MailComposeController alloc] initForContentSize:[overlay_ bounds].size];
2677 [controller_ setDelegate:self];
2678 [controller_ initializeUI];
2679 [controller_ setupForURL:url];
2680
2681 UIView *view([controller_ view]);
2682 [overlay_ addSubview:view];
2683 } return self;
2684}
2685
2686@end
2687/* }}} */
36bb2ca2 2688/* Confirmation View {{{ */
a9a0661e
JF
2689bool DepSubstrate(const pkgCache::VerIterator &iterator) {
2690 if (!iterator.end())
2691 for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
2692 if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
2693 continue;
2694 pkgCache::PkgIterator package(dep.TargetPkg());
2695 if (package.end())
2696 continue;
2697 if (strcmp(package.Name(), "mobilesubstrate") == 0)
2698 return true;
2699 }
2700
2701 return false;
2702}
2703
36bb2ca2
JF
2704@protocol ConfirmationViewDelegate
2705- (void) cancel;
2706- (void) confirm;
2707@end
2367a917 2708
affeffc7 2709@interface ConfirmationView : BrowserView {
770f2a8e 2710 _transient Database *database_;
a9543575 2711 UIActionSheet *essential_;
affeffc7
JF
2712 NSArray *changes_;
2713 NSArray *issues_;
2714 NSArray *sizes_;
a9a0661e 2715 BOOL substrate_;
36bb2ca2 2716}
dc5812ec 2717
affeffc7 2718- (id) initWithBook:(RVBook *)book database:(Database *)database;
b4d89997 2719
dc5812ec
JF
2720@end
2721
36bb2ca2 2722@implementation ConfirmationView
dc5812ec 2723
2367a917 2724- (void) dealloc {
affeffc7
JF
2725 [changes_ release];
2726 if (issues_ != nil)
2727 [issues_ release];
2728 [sizes_ release];
36bb2ca2
JF
2729 if (essential_ != nil)
2730 [essential_ release];
2367a917
JF
2731 [super dealloc];
2732}
2733
36bb2ca2 2734- (void) cancel {
36bb2ca2 2735 [delegate_ cancel];
affeffc7 2736 [book_ popFromSuperviewAnimated:YES];
36bb2ca2
JF
2737}
2738
a9543575 2739- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821 2740 NSString *context([sheet context]);
9bedffaa 2741
1cedb821 2742 if ([context isEqualToString:@"remove"]) {
9bedffaa
JF
2743 switch (button) {
2744 case 1:
31f3cfff 2745 [self cancel];
9bedffaa
JF
2746 break;
2747 case 2:
a9a0661e
JF
2748 if (substrate_)
2749 Finish_ = 2;
31f3cfff 2750 [delegate_ confirm];
9bedffaa
JF
2751 break;
2752 default:
2753 _assert(false);
2754 }
9bedffaa 2755
1cedb821
JF
2756 [sheet dismiss];
2757 } else if ([context isEqualToString:@"unable"]) {
2758 [self cancel];
2759 [sheet dismiss];
2760 } else
2761 [super alertSheet:sheet buttonClicked:button];
36bb2ca2
JF
2762}
2763
affeffc7
JF
2764- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
2765 [window setValue:changes_ forKey:@"changes"];
2766 [window setValue:issues_ forKey:@"issues"];
2767 [window setValue:sizes_ forKey:@"sizes"];
2768 [super webView:sender didClearWindowObject:window forFrame:frame];
36bb2ca2
JF
2769}
2770
affeffc7 2771- (id) initWithBook:(RVBook *)book database:(Database *)database {
770f2a8e
JF
2772 if ((self = [super initWithBook:book]) != nil) {
2773 database_ = database;
2774
36bb2ca2
JF
2775 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
2776 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
2777 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
2778 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
2779 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
dc5812ec 2780
36bb2ca2 2781 bool remove(false);
dc5812ec 2782
a9a0661e
JF
2783 pkgDepCache::Policy *policy([database_ policy]);
2784
36bb2ca2 2785 pkgCacheFile &cache([database_ cache]);
2388b078
JF
2786 NSArray *packages = [database_ packages];
2787 for (size_t i(0), e = [packages count]; i != e; ++i) {
2788 Package *package = [packages objectAtIndex:i];
2789 pkgCache::PkgIterator iterator = [package iterator];
36bb2ca2 2790 pkgDepCache::StateCache &state(cache[iterator]);
dc5812ec 2791
2388b078
JF
2792 NSString *name([package name]);
2793
36bb2ca2
JF
2794 if (state.NewInstall())
2795 [installing addObject:name];
2796 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2797 [reinstalling addObject:name];
2798 else if (state.Upgrade())
2799 [upgrading addObject:name];
2800 else if (state.Downgrade())
2801 [downgrading addObject:name];
2802 else if (state.Delete()) {
2388b078 2803 if ([package essential])
36bb2ca2
JF
2804 remove = true;
2805 [removing addObject:name];
a9a0661e
JF
2806 } else continue;
2807
2808 substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
2809 substrate_ |= DepSubstrate(iterator.CurrentVer());
36bb2ca2 2810 }
ec97ef06 2811
36bb2ca2
JF
2812 if (!remove)
2813 essential_ = nil;
9bedffaa 2814 else if (Advanced_ || true) {
a9543575 2815 essential_ = [[UIActionSheet alloc]
31f3cfff 2816 initWithTitle:@"Removing Essentials"
9bedffaa 2817 buttons:[NSArray arrayWithObjects:
31f3cfff
JF
2818 @"Cancel Operation (Safe)",
2819 @"Force Removal (Unsafe)",
9bedffaa 2820 nil]
31f3cfff 2821 defaultButtonIndex:0
9bedffaa
JF
2822 delegate:self
2823 context:@"remove"
2824 ];
2825
199d0ba5 2826#ifndef __OBJC2__
9bedffaa 2827 [essential_ setDestructiveButton:[[essential_ buttons] objectAtIndex:0]];
199d0ba5 2828#endif
31f3cfff 2829 [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 2830 } else {
a9543575 2831 essential_ = [[UIActionSheet alloc]
36bb2ca2
JF
2832 initWithTitle:@"Unable to Comply"
2833 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2834 defaultButtonIndex:0
2835 delegate:self
9bedffaa 2836 context:@"unable"
36bb2ca2 2837 ];
ec97ef06 2838
31f3cfff 2839 [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 2840 }
ec97ef06 2841
affeffc7
JF
2842 changes_ = [[NSArray alloc] initWithObjects:
2843 installing,
2844 reinstalling,
2845 upgrading,
2846 downgrading,
2847 removing,
2848 nil];
dc5812ec 2849
affeffc7
JF
2850 issues_ = [database_ issues];
2851 if (issues_ != nil)
2852 issues_ = [issues_ retain];
dc5812ec 2853
affeffc7
JF
2854 sizes_ = [[NSArray alloc] initWithObjects:
2855 SizeString([database_ fetcher].FetchNeeded()),
2856 SizeString([database_ fetcher].PartialPresent()),
2857 SizeString([database_ cache]->UsrSize()),
2858 nil];
dc5812ec 2859
affeffc7 2860 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"confirm" ofType:@"html"]]];
36bb2ca2 2861 } return self;
b4d89997 2862}
8da60fb7 2863
affeffc7
JF
2864- (NSString *) backButtonTitle {
2865 return @"Confirm";
2866}
2867
2868- (NSString *) leftButtonTitle {
2869 return @"Cancel";
2870}
2871
fd7853a6 2872- (id) _rightButtonTitle {
affeffc7
JF
2873 return issues_ == nil ? @"Confirm" : nil;
2874}
2875
2876- (void) _leftButtonClicked {
2877 [self cancel];
2878}
2879
9487f027 2880#if !AlwaysReload
affeffc7
JF
2881- (void) _rightButtonClicked {
2882 if (essential_ != nil)
2883 [essential_ popupAlertAnimated:YES];
2884 else {
2885 if (substrate_)
2886 Finish_ = 2;
2887 [delegate_ confirm];
2888 }
2889}
9487f027 2890#endif
affeffc7 2891
36bb2ca2
JF
2892@end
2893/* }}} */
8da60fb7 2894
36bb2ca2
JF
2895/* Progress Data {{{ */
2896@interface ProgressData : NSObject {
2897 SEL selector_;
2898 id target_;
2899 id object_;
dc5812ec
JF
2900}
2901
36bb2ca2 2902- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
b4d89997 2903
36bb2ca2
JF
2904- (SEL) selector;
2905- (id) target;
2906- (id) object;
2907@end
b4d89997 2908
36bb2ca2 2909@implementation ProgressData
b4d89997 2910
36bb2ca2
JF
2911- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2912 if ((self = [super init]) != nil) {
2913 selector_ = selector;
2914 target_ = target;
2915 object_ = object;
2916 } return self;
2917}
b4d89997 2918
36bb2ca2
JF
2919- (SEL) selector {
2920 return selector_;
2921}
b4d89997 2922
36bb2ca2
JF
2923- (id) target {
2924 return target_;
8da60fb7
JF
2925}
2926
36bb2ca2
JF
2927- (id) object {
2928 return object_;
dc5812ec
JF
2929}
2930
2367a917
JF
2931@end
2932/* }}} */
36bb2ca2
JF
2933/* Progress View {{{ */
2934@interface ProgressView : UIView <
8b29f8e6 2935 ConfigurationDelegate,
36bb2ca2
JF
2936 ProgressDelegate
2937> {
8b29f8e6 2938 _transient Database *database_;
36bb2ca2
JF
2939 UIView *view_;
2940 UIView *background_;
2941 UITransitionView *transition_;
2942 UIView *overlay_;
2943 UINavigationBar *navbar_;
2944 UIProgressBar *progress_;
2945 UITextView *output_;
2946 UITextLabel *status_;
31f3cfff 2947 UIPushButton *close_;
36bb2ca2 2948 id delegate_;
bd150f54 2949 BOOL running_;
dddbc481 2950 SHA1SumValue springlist_;
affeffc7 2951 SHA1SumValue sandplate_;
baf80942
JF
2952 size_t received_;
2953 NSTimeInterval last_;
2367a917
JF
2954}
2955
36bb2ca2 2956- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2367a917 2957
8b29f8e6 2958- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
36bb2ca2
JF
2959- (void) setContentView:(UIView *)view;
2960- (void) resetView;
2367a917 2961
36bb2ca2
JF
2962- (void) _retachThread;
2963- (void) _detachNewThreadData:(ProgressData *)data;
2964- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2367a917 2965
bd150f54
JF
2966- (BOOL) isRunning;
2967
2367a917
JF
2968@end
2969
36bb2ca2
JF
2970@protocol ProgressViewDelegate
2971- (void) progressViewIsComplete:(ProgressView *)sender;
2972@end
2973
2974@implementation ProgressView
2367a917
JF
2975
2976- (void) dealloc {
36bb2ca2
JF
2977 [transition_ setDelegate:nil];
2978 [navbar_ setDelegate:nil];
2979
2980 [view_ release];
2981 if (background_ != nil)
2982 [background_ release];
2983 [transition_ release];
2984 [overlay_ release];
2985 [navbar_ release];
2986 [progress_ release];
2987 [output_ release];
2988 [status_ release];
31f3cfff 2989 [close_ release];
2367a917
JF
2990 [super dealloc];
2991}
2992
36bb2ca2
JF
2993- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2994 if (bootstrap_ && from == overlay_ && to == view_)
2995 exit(0);
2996}
2367a917 2997
8b29f8e6 2998- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
36bb2ca2 2999 if ((self = [super initWithFrame:frame]) != nil) {
8b29f8e6 3000 database_ = database;
36bb2ca2 3001 delegate_ = delegate;
ec97ef06 3002
36bb2ca2
JF
3003 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
3004 [transition_ setDelegate:self];
2367a917 3005
36bb2ca2 3006 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
ec97ef06 3007
36bb2ca2 3008 if (bootstrap_)
baf80942 3009 [overlay_ setBackgroundColor:[UIColor blackColor]];
36bb2ca2
JF
3010 else {
3011 background_ = [[UIView alloc] initWithFrame:[self bounds]];
baf80942 3012 [background_ setBackgroundColor:[UIColor blackColor]];
36bb2ca2
JF
3013 [self addSubview:background_];
3014 }
ec97ef06 3015
36bb2ca2 3016 [self addSubview:transition_];
2367a917 3017
36bb2ca2
JF
3018 CGSize navsize = [UINavigationBar defaultSize];
3019 CGRect navrect = {{0, 0}, navsize};
2367a917 3020
36bb2ca2
JF
3021 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
3022 [overlay_ addSubview:navbar_];
b4d89997 3023
36bb2ca2
JF
3024 [navbar_ setBarStyle:1];
3025 [navbar_ setDelegate:self];
2367a917 3026
36bb2ca2
JF
3027 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
3028 [navbar_ pushNavigationItem:navitem];
c7c6384e 3029
36bb2ca2
JF
3030 CGRect bounds = [overlay_ bounds];
3031 CGSize prgsize = [UIProgressBar defaultSize];
ec97ef06 3032
36bb2ca2
JF
3033 CGRect prgrect = {{
3034 (bounds.size.width - prgsize.width) / 2,
3035 bounds.size.height - prgsize.height - 20
3036 }, prgsize};
ec97ef06 3037
36bb2ca2 3038 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
31f3cfff 3039 [progress_ setStyle:0];
ec97ef06 3040
36bb2ca2
JF
3041 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
3042 10,
3043 bounds.size.height - prgsize.height - 50,
3044 bounds.size.width - 20,
3045 24
3046 )];
ec97ef06 3047
baf80942
JF
3048 [status_ setColor:[UIColor whiteColor]];
3049 [status_ setBackgroundColor:[UIColor clearColor]];
ec97ef06 3050
36bb2ca2
JF
3051 [status_ setCentersHorizontally:YES];
3052 //[status_ setFont:font];
ec97ef06 3053
36bb2ca2
JF
3054 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
3055 10,
3056 navrect.size.height + 20,
3057 bounds.size.width - 20,
3058 bounds.size.height - navsize.height - 62 - navrect.size.height
3059 )];
1d80f6b9 3060
36bb2ca2
JF
3061 //[output_ setTextFont:@"Courier New"];
3062 [output_ setTextSize:12];
2367a917 3063
baf80942
JF
3064 [output_ setTextColor:[UIColor whiteColor]];
3065 [output_ setBackgroundColor:[UIColor clearColor]];
2367a917 3066
36bb2ca2
JF
3067 [output_ setMarginTop:0];
3068 [output_ setAllowsRubberBanding:YES];
795d26fc 3069 [output_ setEditable:NO];
b4d89997 3070
36bb2ca2 3071 [overlay_ addSubview:output_];
2367a917 3072
31f3cfff
JF
3073 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
3074 10,
3075 bounds.size.height - prgsize.height - 50,
3076 bounds.size.width - 20,
795d26fc 3077 32 + prgsize.height
31f3cfff
JF
3078 )];
3079
3080 [close_ setAutosizesToFit:NO];
3081 [close_ setDrawsShadow:YES];
3082 [close_ setStretchBackground:YES];
31f3cfff
JF
3083 [close_ setEnabled:YES];
3084
a9543575 3085 UIFont *bold = [UIFont boldSystemFontOfSize:22];
31f3cfff 3086 [close_ setTitleFont:bold];
31f3cfff
JF
3087
3088 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
3089 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
3090 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
36bb2ca2 3091 } return self;
2367a917
JF
3092}
3093
36bb2ca2
JF
3094- (void) setContentView:(UIView *)view {
3095 view_ = [view retain];
2367a917
JF
3096}
3097
36bb2ca2
JF
3098- (void) resetView {
3099 [transition_ transition:6 toView:view_];
2367a917
JF
3100}
3101
a9543575 3102- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821
JF
3103 NSString *context([sheet context]);
3104
8b29f8e6
JF
3105 if ([context isEqualToString:@"conffile"]) {
3106 FILE *input = [database_ input];
3107
3108 switch (button) {
3109 case 1:
3110 fprintf(input, "N\n");
3111 fflush(input);
9bedffaa 3112 break;
8b29f8e6
JF
3113 case 2:
3114 fprintf(input, "Y\n");
3115 fflush(input);
9bedffaa
JF
3116 break;
3117 default:
3118 _assert(false);
8b29f8e6 3119 }
8b29f8e6 3120
1cedb821
JF
3121 [sheet dismiss];
3122 }
2367a917
JF
3123}
3124
31f3cfff 3125- (void) closeButtonPushed {
affeffc7
JF
3126 running_ = NO;
3127
bc8cd583
JF
3128 switch (Finish_) {
3129 case 0:
a9a0661e 3130 [self resetView];
bc8cd583
JF
3131 break;
3132
3133 case 1:
3134 [delegate_ suspendWithAnimation:YES];
3135 break;
3136
3137 case 2:
a9a0661e 3138 system("launchctl stop com.apple.SpringBoard");
bc8cd583
JF
3139 break;
3140
3141 case 3:
affeffc7 3142 system("launchctl unload "SpringBoard_"; launchctl load "SpringBoard_);
bc8cd583
JF
3143 break;
3144
3145 case 4:
3146 system("reboot");
3147 break;
3148 }
b19871dd
JF
3149}
3150
31f3cfff 3151- (void) _retachThread {
795d26fc
JF
3152 UINavigationItem *item = [navbar_ topItem];
3153 [item setTitle:@"Complete"];
3154
31f3cfff
JF
3155 [overlay_ addSubview:close_];
3156 [progress_ removeFromSuperview];
3157 [status_ removeFromSuperview];
bd150f54 3158
c390d3ab
JF
3159 [delegate_ progressViewIsComplete:self];
3160
affeffc7
JF
3161 if (Finish_ < 4) {
3162 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3163 MMap mmap(file, MMap::ReadOnly);
3164 SHA1Summation sha1;
3165 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3166 if (!(sandplate_ == sha1.Result()))
3167 Finish_ = 4;
3168 }
3169
3170 if (Finish_ < 3) {
3171 FileFd file(SpringBoard_, FileFd::ReadOnly);
dddbc481
JF
3172 MMap mmap(file, MMap::ReadOnly);
3173 SHA1Summation sha1;
3174 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3175 if (!(springlist_ == sha1.Result()))
3176 Finish_ = 3;
3177 }
3178
bc8cd583
JF
3179 switch (Finish_) {
3180 case 0: [close_ setTitle:@"Return to Cydia"]; break;
3181 case 1: [close_ setTitle:@"Close Cydia (Restart)"]; break;
3182 case 2: [close_ setTitle:@"Restart SpringBoard"]; break;
3183 case 3: [close_ setTitle:@"Reload SpringBoard"]; break;
3184 case 4: [close_ setTitle:@"Reboot Device"]; break;
3185 }
3186
87f46a96
JF
3187#define Cache_ "/User/Library/Caches/com.apple.mobile.installation.plist"
3188
95cafe4f 3189 if (NSMutableDictionary *cache = [[NSMutableDictionary alloc] initWithContentsOfFile:@ Cache_]) {
87f46a96
JF
3190 [cache autorelease];
3191
3192 NSFileManager *manager = [NSFileManager defaultManager];
6e201c55 3193 NSError *error = nil;
87f46a96 3194
6e201c55 3195 id system = [cache objectForKey:@"System"];
87f46a96
JF
3196 if (system == nil)
3197 goto error;
3198
3199 struct stat info;
3200 if (stat(Cache_, &info) == -1)
3201 goto error;
3202
3203 [system removeAllObjects];
3204
6e201c55 3205 if (NSArray *apps = [manager contentsOfDirectoryAtPath:@"/Applications" error:&error]) {
87f46a96
JF
3206 for (NSString *app in apps)
3207 if ([app hasSuffix:@".app"]) {
3208 NSString *path = [@"/Applications" stringByAppendingPathComponent:app];
3209 NSString *plist = [path stringByAppendingPathComponent:@"Info.plist"];
3210 if (NSMutableDictionary *info = [[NSMutableDictionary alloc] initWithContentsOfFile:plist]) {
3211 [info autorelease];
3622197e
JF
3212 if ([info objectForKey:@"CFBundleIdentifier"] != nil) {
3213 [info setObject:path forKey:@"Path"];
3214 [info setObject:@"System" forKey:@"ApplicationType"];
3215 [system addInfoDictionary:info];
3216 }
87f46a96
JF
3217 }
3218 }
6e201c55 3219 } else goto error;
87f46a96
JF
3220
3221 [cache writeToFile:@Cache_ atomically:YES];
3222
3223 if (chown(Cache_, info.st_uid, info.st_gid) == -1)
3224 goto error;
3225 if (chmod(Cache_, info.st_mode) == -1)
3226 goto error;
3227
3228 if (false) error:
c390d3ab 3229 lprintf("%s\n", error == nil ? strerror(errno) : [[error localizedDescription] UTF8String]);
87f46a96
JF
3230 }
3231
c4ce98df 3232 notify_post("com.apple.mobile.application_installed");
c4ce98df 3233
bd150f54 3234 [delegate_ setStatusBarShowsProgress:NO];
31f3cfff
JF
3235}
3236
affeffc7 3237- (void) _detachNewThreadData:(ProgressData *)data { _pooled
36bb2ca2
JF
3238 [[data target] performSelector:[data selector] withObject:[data object]];
3239 [data release];
b19871dd 3240
36bb2ca2 3241 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
b19871dd
JF
3242}
3243
36bb2ca2 3244- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
5a09ae08
JF
3245 UINavigationItem *item = [navbar_ topItem];
3246 [item setTitle:title];
b4d89997 3247
36bb2ca2
JF
3248 [status_ setText:nil];
3249 [output_ setText:@""];
3250 [progress_ setProgress:0];
b19871dd 3251
baf80942
JF
3252 received_ = 0;
3253 last_ = 0;//[NSDate timeIntervalSinceReferenceDate];
3254
31f3cfff
JF
3255 [close_ removeFromSuperview];
3256 [overlay_ addSubview:progress_];
3257 [overlay_ addSubview:status_];
3258
bd150f54
JF
3259 [delegate_ setStatusBarShowsProgress:YES];
3260 running_ = YES;
3261
dddbc481 3262 {
affeffc7
JF
3263 FileFd file(SandboxTemplate_, FileFd::ReadOnly);
3264 MMap mmap(file, MMap::ReadOnly);
3265 SHA1Summation sha1;
3266 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3267 sandplate_ = sha1.Result();
3268 }
3269
3270 {
3271 FileFd file(SpringBoard_, FileFd::ReadOnly);
dddbc481
JF
3272 MMap mmap(file, MMap::ReadOnly);
3273 SHA1Summation sha1;
3274 sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
3275 springlist_ = sha1.Result();
3276 }
3277
36bb2ca2 3278 [transition_ transition:6 toView:overlay_];
b19871dd 3279
36bb2ca2
JF
3280 [NSThread
3281 detachNewThreadSelector:@selector(_detachNewThreadData:)
3282 toTarget:self
3283 withObject:[[ProgressData alloc]
3284 initWithSelector:selector
3285 target:target
3286 object:object
3287 ]
3288 ];
b19871dd
JF
3289}
3290
5a09ae08
JF
3291- (void) repairWithSelector:(SEL)selector {
3292 [self
3293 detachNewThreadSelector:selector
3294 toTarget:database_
3295 withObject:nil
baf80942 3296 title:@"Repairing"
5a09ae08
JF
3297 ];
3298}
3299
8b29f8e6
JF
3300- (void) setConfigurationData:(NSString *)data {
3301 [self
3302 performSelectorOnMainThread:@selector(_setConfigurationData:)
3303 withObject:data
3304 waitUntilDone:YES
3305 ];
3306}
3307
31f3cfff
JF
3308- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
3309 Package *package = id == nil ? nil : [database_ packageWithName:id];
3310
a9543575 3311 UIActionSheet *sheet = [[[UIActionSheet alloc]
affeffc7 3312 initWithTitle:(package == nil ? id : [package name])
31f3cfff
JF
3313 buttons:[NSArray arrayWithObjects:@"Okay", nil]
3314 defaultButtonIndex:0
3315 delegate:self
3316 context:@"error"
3317 ] autorelease];
3318
3319 [sheet setBodyText:error];
3320 [sheet popupAlertAnimated:YES];
b19871dd
JF
3321}
3322
36bb2ca2
JF
3323- (void) setProgressTitle:(NSString *)title {
3324 [self
3325 performSelectorOnMainThread:@selector(_setProgressTitle:)
3326 withObject:title
3327 waitUntilDone:YES
3328 ];
b19871dd
JF
3329}
3330
36bb2ca2
JF
3331- (void) setProgressPercent:(float)percent {
3332 [self
3333 performSelectorOnMainThread:@selector(_setProgressPercent:)
3334 withObject:[NSNumber numberWithFloat:percent]
3335 waitUntilDone:YES
3336 ];
b19871dd
JF
3337}
3338
baf80942
JF
3339- (void) startProgress {
3340 last_ = [NSDate timeIntervalSinceReferenceDate];
3341}
3342
36bb2ca2
JF
3343- (void) addProgressOutput:(NSString *)output {
3344 [self
3345 performSelectorOnMainThread:@selector(_addProgressOutput:)
3346 withObject:output
3347 waitUntilDone:YES
3348 ];
b19871dd
JF
3349}
3350
baf80942
JF
3351- (bool) isCancelling:(size_t)received {
3352 if (last_ != 0) {
3353 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
3354 if (received_ != received) {
3355 received_ = received;
3356 last_ = now;
3357 } else if (now - last_ > 30)
3358 return true;
3359 }
3360
3361 return false;
3362}
3363
8b29f8e6 3364- (void) _setConfigurationData:(NSString *)data {
7b0ce2da
JF
3365 static Pcre conffile_r("^'(.*)' '(.*)' ([01]) ([01])$");
3366
8b29f8e6
JF
3367 _assert(conffile_r(data));
3368
3369 NSString *ofile = conffile_r[1];
3370 //NSString *nfile = conffile_r[2];
3371
a9543575 3372 UIActionSheet *sheet = [[[UIActionSheet alloc]
8b29f8e6
JF
3373 initWithTitle:@"Configuration Upgrade"
3374 buttons:[NSArray arrayWithObjects:
3375 @"Keep My Old Copy",
3376 @"Accept The New Copy",
3377 // XXX: @"See What Changed",
3378 nil]
3379 defaultButtonIndex:0
3380 delegate:self
3381 context:@"conffile"
3382 ] autorelease];
3383
3384 [sheet setBodyText:[NSString stringWithFormat:
3385 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
3386 , ofile]];
3387
3388 [sheet popupAlertAnimated:YES];
3389}
3390
36bb2ca2 3391- (void) _setProgressTitle:(NSString *)title {
9487f027
JF
3392 NSMutableArray *words([[title componentsSeparatedByString:@" "] mutableCopy]);
3393 for (size_t i(0), e([words count]); i != e; ++i) {
3394 NSString *word([words objectAtIndex:i]);
3395 if (Package *package = [database_ packageWithName:word])
3396 [words replaceObjectAtIndex:i withObject:[package name]];
3397 }
3398
3399 [status_ setText:[words componentsJoinedByString:@" "]];
b4d89997 3400}
b19871dd 3401
36bb2ca2
JF
3402- (void) _setProgressPercent:(NSNumber *)percent {
3403 [progress_ setProgress:[percent floatValue]];
49048579
JF
3404}
3405
36bb2ca2
JF
3406- (void) _addProgressOutput:(NSString *)output {
3407 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
3408 CGSize size = [output_ contentSize];
3409 CGRect rect = {{0, size.height}, {size.width, 0}};
3410 [output_ scrollRectToVisible:rect animated:YES];
3411}
dc088e63 3412
bd150f54 3413- (BOOL) isRunning {
c4ce98df 3414 return running_;
bd150f54
JF
3415}
3416
36bb2ca2
JF
3417@end
3418/* }}} */
dc088e63 3419
36bb2ca2 3420/* Package Cell {{{ */
f641a0e5
JF
3421@interface PackageCell : UISimpleTableCell {
3422 UIImage *icon_;
3423 NSString *name_;
3424 NSString *description_;
3425 NSString *source_;
c390d3ab 3426 UIImage *badge_;
31f3cfff 3427#ifdef USE_BADGES
9bedffaa 3428 UITextLabel *status_;
31f3cfff 3429#endif
36bb2ca2 3430}
723a0072 3431
36bb2ca2
JF
3432- (PackageCell *) init;
3433- (void) setPackage:(Package *)package;
ec97ef06 3434
9bedffaa
JF
3435+ (int) heightForPackage:(Package *)package;
3436
36bb2ca2 3437@end
49048579 3438
36bb2ca2
JF
3439@implementation PackageCell
3440
f641a0e5
JF
3441- (void) clearPackage {
3442 if (icon_ != nil) {
3443 [icon_ release];
3444 icon_ = nil;
3445 }
3446
3447 if (name_ != nil) {
3448 [name_ release];
3449 name_ = nil;
3450 }
3451
3452 if (description_ != nil) {
3453 [description_ release];
3454 description_ = nil;
3455 }
3456
3457 if (source_ != nil) {
3458 [source_ release];
3459 source_ = nil;
3460 }
c390d3ab
JF
3461
3462 if (badge_ != nil) {
3463 [badge_ release];
3464 badge_ = nil;
3465 }
f641a0e5
JF
3466}
3467
36bb2ca2 3468- (void) dealloc {
f641a0e5 3469 [self clearPackage];
31f3cfff 3470#ifdef USE_BADGES
9bedffaa 3471 [status_ release];
31f3cfff 3472#endif
36bb2ca2 3473 [super dealloc];
b4d89997 3474}
b19871dd 3475
36bb2ca2
JF
3476- (PackageCell *) init {
3477 if ((self = [super init]) != nil) {
31f3cfff 3478#ifdef USE_BADGES
9bedffaa 3479 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
baf80942 3480 [status_ setBackgroundColor:[UIColor clearColor]];
9bedffaa 3481 [status_ setFont:small];
31f3cfff 3482#endif
36bb2ca2 3483 } return self;
b4d89997 3484}
b19871dd 3485
36bb2ca2 3486- (void) setPackage:(Package *)package {
46dbfd32 3487 [self clearPackage];
31f3cfff 3488
36bb2ca2 3489 Source *source = [package source];
dec6029f 3490 NSString *section = [package simpleSection];
b19871dd 3491
770f2a8e 3492 icon_ = [[package icon] retain];
b19871dd 3493
f641a0e5
JF
3494 name_ = [[package name] retain];
3495 description_ = [[package tagline] retain];
36bb2ca2 3496
a54b1c10
JF
3497 NSString *label = nil;
3498 bool trusted = false;
b19871dd 3499
36bb2ca2
JF
3500 if (source != nil) {
3501 label = [source label];
3502 trusted = [source trusted];
a54b1c10 3503 } else if ([[package id] isEqualToString:@"firmware"])
36bb2ca2 3504 label = @"Apple";
7b0ce2da 3505 else
36bb2ca2 3506 label = @"Unknown/Local";
b19871dd 3507
a54b1c10
JF
3508 NSString *from = [NSString stringWithFormat:@"from %@", label];
3509
a54b1c10
JF
3510 if (section != nil && ![section isEqualToString:label])
3511 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
3512
f641a0e5 3513 source_ = [from retain];
36bb2ca2 3514
c390d3ab
JF
3515 if (NSString *purpose = [package primaryPurpose])
3516 if ((badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]]) != nil)
3517 badge_ = [badge_ retain];
3518
31f3cfff 3519#ifdef USE_BADGES
9bedffaa
JF
3520 if (NSString *mode = [package mode]) {
3521 [badge_ setImage:[UIImage applicationImageNamed:
3522 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
3523 ]];
3524
3525 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
affeffc7 3526 [status_ setColor:[UIColor colorWithCGColor:Blueish_]];
9bedffaa
JF
3527 } else if ([package half]) {
3528 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
3529 [status_ setText:@"Package Damaged"];
baf80942 3530 [status_ setColor:[UIColor redColor]];
9bedffaa
JF
3531 } else {
3532 [badge_ setImage:nil];
3533 [status_ setText:nil];
3534 }
31f3cfff 3535#endif
b19871dd
JF
3536}
3537
f641a0e5 3538- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
baf80942
JF
3539 if (icon_ != nil) {
3540 CGRect rect;
3541 rect.size = [icon_ size];
3542
3543 rect.size.width /= 2;
3544 rect.size.height /= 2;
3545
3546 rect.origin.x = 25 - rect.size.width / 2;
3547 rect.origin.y = 25 - rect.size.height / 2;
3548
3549 [icon_ drawInRect:rect];
3550 }
b19871dd 3551
c390d3ab
JF
3552 if (badge_ != nil) {
3553 CGSize size = [badge_ size];
3554
3555 [badge_ drawAtPoint:CGPointMake(
3556 36 - size.width / 2,
3557 36 - size.height / 2
3558 )];
3559 }
3560
f641a0e5
JF
3561 if (selected)
3562 UISetColor(White_);
b19871dd 3563
f641a0e5
JF
3564 if (!selected)
3565 UISetColor(Black_);
3566 [name_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
3567 [source_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
b19871dd 3568
f641a0e5
JF
3569 if (!selected)
3570 UISetColor(Gray_);
3571 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
b19871dd 3572
f641a0e5 3573 [super drawContentInRect:rect selected:selected];
ec97ef06
JF
3574}
3575
9bedffaa 3576+ (int) heightForPackage:(Package *)package {
6d9712c4 3577 NSString *tagline([package tagline]);
f641a0e5 3578 int height = tagline == nil || [tagline length] == 0 ? -17 : 0;
31f3cfff 3579#ifdef USE_BADGES
9bedffaa 3580 if ([package hasMode] || [package half])
6d9712c4 3581 return height + 96;
9bedffaa 3582 else
31f3cfff 3583#endif
6d9712c4 3584 return height + 73;
9bedffaa
JF
3585}
3586
8da60fb7
JF
3587@end
3588/* }}} */
36bb2ca2 3589/* Section Cell {{{ */
f641a0e5 3590@interface SectionCell : UISimpleTableCell {
6d9712c4 3591 NSString *section_;
f641a0e5
JF
3592 NSString *name_;
3593 NSString *count_;
3594 UIImage *icon_;
a9543575 3595 _UISwitchSlider *switch_;
6d9712c4 3596 BOOL editing_;
b4d89997 3597}
b19871dd 3598
36bb2ca2 3599- (id) init;
6d9712c4 3600- (void) setSection:(Section *)section editing:(BOOL)editing;
36bb2ca2 3601
8da60fb7
JF
3602@end
3603
36bb2ca2 3604@implementation SectionCell
8da60fb7 3605
f641a0e5
JF
3606- (void) clearSection {
3607 if (section_ != nil) {
6d9712c4 3608 [section_ release];
f641a0e5
JF
3609 section_ = nil;
3610 }
7b0ce2da 3611
f641a0e5
JF
3612 if (name_ != nil) {
3613 [name_ release];
3614 name_ = nil;
3615 }
7b0ce2da 3616
f641a0e5
JF
3617 if (count_ != nil) {
3618 [count_ release];
3619 count_ = nil;
3620 }
7b0ce2da
JF
3621}
3622
f641a0e5
JF
3623- (void) dealloc {
3624 [self clearSection];
3625 [icon_ release];
3626 [switch_ release];
3627 [super dealloc];
7b0ce2da
JF
3628}
3629
36bb2ca2 3630- (id) init {
b4d89997 3631 if ((self = [super init]) != nil) {
f641a0e5 3632 icon_ = [[UIImage applicationImageNamed:@"folder.png"] retain];
36bb2ca2 3633
a9543575 3634 switch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(218, 9, 60, 25)];
6d9712c4 3635 [switch_ addTarget:self action:@selector(onSwitch:) forEvents:kUIControlEventMouseUpInside];
b4d89997 3636 } return self;
b19871dd
JF
3637}
3638
6d9712c4
JF
3639- (void) onSwitch:(id)sender {
3640 NSMutableDictionary *metadata = [Sections_ objectForKey:section_];
3641 if (metadata == nil) {
3642 metadata = [NSMutableDictionary dictionaryWithCapacity:2];
3643 [Sections_ setObject:metadata forKey:section_];
3644 }
3645
3646 Changed_ = true;
3647 [metadata setObject:[NSNumber numberWithBool:([switch_ value] == 0)] forKey:@"Hidden"];
3648}
3649
3650- (void) setSection:(Section *)section editing:(BOOL)editing {
3651 if (editing != editing_) {
3652 if (editing_)
3653 [switch_ removeFromSuperview];
3654 else
3655 [self addSubview:switch_];
3656 editing_ = editing;
3657 }
3658
f641a0e5 3659 [self clearSection];
6d9712c4 3660
36bb2ca2 3661 if (section == nil) {
f641a0e5
JF
3662 name_ = [@"All Packages" retain];
3663 count_ = nil;
36bb2ca2 3664 } else {
6d9712c4
JF
3665 section_ = [section name];
3666 if (section_ != nil)
3667 section_ = [section_ retain];
f641a0e5
JF
3668 name_ = [(section_ == nil ? @"(No Section)" : section_) retain];
3669 count_ = [[NSString stringWithFormat:@"%d", [section count]] retain];
6d9712c4
JF
3670
3671 if (editing_)
807ae6d7 3672 [switch_ setValue:(isSectionVisible(section_) ? 1 : 0) animated:NO];
36bb2ca2 3673 }
f641a0e5
JF
3674}
3675
3676- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
3677 [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
3678
3679 if (selected)
3680 UISetColor(White_);
3681
3682 if (!selected)
3683 UISetColor(Black_);
3684 [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(editing_ ? 164 : 250) withFont:Font22Bold_ ellipsis:2];
6d9712c4 3685
f641a0e5
JF
3686 CGSize size = [count_ sizeWithFont:Font14_];
3687
3688 UISetColor(White_);
3689 if (count_ != nil)
c390d3ab 3690 [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
f641a0e5
JF
3691
3692 [super drawContentInRect:rect selected:selected];
b19871dd
JF
3693}
3694
36bb2ca2
JF
3695@end
3696/* }}} */
b19871dd 3697
ab398adf
JF
3698/* File Table {{{ */
3699@interface FileTable : RVPage {
36bb2ca2 3700 _transient Database *database_;
ab398adf
JF
3701 Package *package_;
3702 NSString *name_;
3703 NSMutableArray *files_;
3704 UITable *list_;
3705}
b19871dd 3706
ab398adf
JF
3707- (id) initWithBook:(RVBook *)book database:(Database *)database;
3708- (void) setPackage:(Package *)package;
3709
3710@end
3711
3712@implementation FileTable
3713
3714- (void) dealloc {
3715 if (package_ != nil)
3716 [package_ release];
3717 if (name_ != nil)
3718 [name_ release];
3719 [files_ release];
3720 [list_ release];
3721 [super dealloc];
3722}
3723
3724- (int) numberOfRowsInTable:(UITable *)table {
3725 return files_ == nil ? 0 : [files_ count];
3726}
3727
3728- (float) table:(UITable *)table heightForRow:(int)row {
3729 return 24;
3730}
3731
3732- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3733 if (reusing == nil) {
3734 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
a9543575 3735 UIFont *font = [UIFont systemFontOfSize:16];
ab398adf 3736 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
ab398adf
JF
3737 }
3738 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
3739 return reusing;
36bb2ca2 3740}
b19871dd 3741
58f6daef 3742- (BOOL) table:(UITable *)table canSelectRow:(int)row {
ab398adf
JF
3743 return NO;
3744}
b19871dd 3745
ab398adf
JF
3746- (id) initWithBook:(RVBook *)book database:(Database *)database {
3747 if ((self = [super initWithBook:book]) != nil) {
3748 database_ = database;
b19871dd 3749
ab398adf 3750 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
8da60fb7 3751
ab398adf
JF
3752 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3753 [self addSubview:list_];
3754
3755 UITableColumn *column = [[[UITableColumn alloc]
3756 initWithTitle:@"Name"
3757 identifier:@"name"
3758 width:[self frame].size.width
3759 ] autorelease];
3760
3761 [list_ setDataSource:self];
3762 [list_ setSeparatorStyle:1];
3763 [list_ addTableColumn:column];
3764 [list_ setDelegate:self];
3765 [list_ setReusesTableCells:YES];
3766 } return self;
3767}
3768
3769- (void) setPackage:(Package *)package {
3770 if (package_ != nil) {
3771 [package_ autorelease];
3772 package_ = nil;
3773 }
3774
3775 if (name_ != nil) {
3776 [name_ release];
3777 name_ = nil;
3778 }
3779
3780 [files_ removeAllObjects];
3781
3782 if (package != nil) {
3783 package_ = [package retain];
3784 name_ = [[package id] retain];
3785
affeffc7
JF
3786 if (NSArray *files = [package files])
3787 [files_ addObjectsFromArray:files];
ab398adf 3788
9ea8d159
JF
3789 if ([files_ count] != 0) {
3790 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
3791 [files_ removeObjectAtIndex:0];
a54b1c10 3792 [files_ sortUsingSelector:@selector(compareByPath:)];
2388b078
JF
3793
3794 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
3795 [stack addObject:@"/"];
3796
3797 for (int i(0), e([files_ count]); i != e; ++i) {
3798 NSString *file = [files_ objectAtIndex:i];
3799 while (![file hasPrefix:[stack lastObject]])
3800 [stack removeLastObject];
3801 NSString *directory = [stack lastObject];
3802 [stack addObject:[file stringByAppendingString:@"/"]];
3803 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
a54b1c10 3804 ([stack count] - 2) * 3, "",
2388b078
JF
3805 [file substringFromIndex:[directory length]]
3806 ]];
3807 }
ab398adf
JF
3808 }
3809 }
3810
3811 [list_ reloadData];
3812}
3813
3814- (void) resetViewAnimated:(BOOL)animated {
3815 [list_ resetViewAnimated:animated];
3816}
3817
3818- (void) reloadData {
3819 [self setPackage:[database_ packageWithName:name_]];
3820 [self reloadButtons];
3821}
3822
3823- (NSString *) title {
2388b078
JF
3824 return @"Installed Files";
3825}
3826
3827- (NSString *) backButtonTitle {
3828 return @"Files";
ab398adf 3829}
b4d89997 3830
b4d89997 3831@end
36bb2ca2 3832/* }}} */
36bb2ca2 3833/* Package View {{{ */
6f1a15d9 3834@interface PackageView : BrowserView {
770f2a8e 3835 _transient Database *database_;
36bb2ca2
JF
3836 Package *package_;
3837 NSString *name_;
5a09ae08 3838 NSMutableArray *buttons_;
b31b87cc
JF
3839}
3840
36bb2ca2
JF
3841- (id) initWithBook:(RVBook *)book database:(Database *)database;
3842- (void) setPackage:(Package *)package;
b4d89997 3843
36bb2ca2 3844@end
b4d89997 3845
36bb2ca2 3846@implementation PackageView
b4d89997 3847
36bb2ca2 3848- (void) dealloc {
36bb2ca2
JF
3849 if (package_ != nil)
3850 [package_ release];
3851 if (name_ != nil)
3852 [name_ release];
5a09ae08 3853 [buttons_ release];
36bb2ca2
JF
3854 [super dealloc];
3855}
8da60fb7 3856
5a09ae08
JF
3857- (void) _clickButtonWithName:(NSString *)name {
3858 if ([name isEqualToString:@"Install"])
3859 [delegate_ installPackage:package_];
3860 else if ([name isEqualToString:@"Reinstall"])
3861 [delegate_ installPackage:package_];
3862 else if ([name isEqualToString:@"Remove"])
3863 [delegate_ removePackage:package_];
3864 else if ([name isEqualToString:@"Upgrade"])
3865 [delegate_ installPackage:package_];
3866 else _assert(false);
3867}
3868
a9543575 3869- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821 3870 NSString *context([sheet context]);
5a09ae08 3871
1cedb821
JF
3872 if ([context isEqualToString:@"modify"]) {
3873 int count = [buttons_ count];
3874 _assert(count != 0);
3875 _assert(button <= count + 1);
36bb2ca2 3876
1cedb821
JF
3877 if (count != button - 1)
3878 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
3879
3880 [sheet dismiss];
3881 } else
3882 [super alertSheet:sheet buttonClicked:button];
b4d89997 3883}
2367a917 3884
7d2ac47f 3885- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
7d2ac47f
JF
3886 return [super webView:sender didFinishLoadForFrame:frame];
3887}
3888
6f1a15d9 3889- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6f1a15d9 3890 [window setValue:package_ forKey:@"package"];
c390d3ab 3891 [super webView:sender didClearWindowObject:window forFrame:frame];
6f1a15d9
JF
3892}
3893
9487f027 3894#if !AlwaysReload
36bb2ca2 3895- (void) _rightButtonClicked {
6f1a15d9
JF
3896 /*[super _rightButtonClicked];
3897 return;*/
3898
5a09ae08
JF
3899 int count = [buttons_ count];
3900 _assert(count != 0);
2367a917 3901
5a09ae08
JF
3902 if (count == 1)
3903 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3904 else {
3905 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3906 [buttons addObjectsFromArray:buttons_];
36bb2ca2
JF
3907 [buttons addObject:@"Cancel"];
3908
a9543575 3909 [delegate_ slideUp:[[[UIActionSheet alloc]
9ea8d159 3910 initWithTitle:nil
36bb2ca2
JF
3911 buttons:buttons
3912 defaultButtonIndex:2
3913 delegate:self
1cedb821 3914 context:@"modify"
36bb2ca2
JF
3915 ] autorelease]];
3916 }
b4d89997 3917}
9487f027 3918#endif
2367a917 3919
fd7853a6 3920- (id) _rightButtonTitle {
5a09ae08
JF
3921 int count = [buttons_ count];
3922 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
b4d89997 3923}
2367a917 3924
affeffc7 3925- (NSString *) backButtonTitle {
36bb2ca2 3926 return @"Details";
dc5812ec
JF
3927}
3928
36bb2ca2 3929- (id) initWithBook:(RVBook *)book database:(Database *)database {
770f2a8e 3930 if ((self = [super initWithBook:book]) != nil) {
36bb2ca2 3931 database_ = database;
5a09ae08 3932 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
36bb2ca2 3933 } return self;
dc5812ec
JF
3934}
3935
36bb2ca2
JF
3936- (void) setPackage:(Package *)package {
3937 if (package_ != nil) {
3938 [package_ autorelease];
3939 package_ = nil;
3940 }
3941
3942 if (name_ != nil) {
3943 [name_ release];
3944 name_ = nil;
3945 }
3946
5a09ae08
JF
3947 [buttons_ removeAllObjects];
3948
36bb2ca2
JF
3949 if (package != nil) {
3950 package_ = [package retain];
3951 name_ = [[package id] retain];
3952
6f1a15d9 3953 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"package" ofType:@"html"]]];
5a09ae08
JF
3954
3955 if ([package_ source] == nil);
31f3cfff
JF
3956 else if ([package_ upgradableAndEssential:NO])
3957 [buttons_ addObject:@"Upgrade"];
5a09ae08
JF
3958 else if ([package_ installed] == nil)
3959 [buttons_ addObject:@"Install"];
5a09ae08
JF
3960 else
3961 [buttons_ addObject:@"Reinstall"];
3962 if ([package_ installed] != nil)
3963 [buttons_ addObject:@"Remove"];
36bb2ca2 3964 }
dc5812ec
JF
3965}
3966
9fe5e5f8
JF
3967- (bool) _loading {
3968 return false;
3969}
3970
36bb2ca2
JF
3971- (void) reloadData {
3972 [self setPackage:[database_ packageWithName:name_]];
3973 [self reloadButtons];
8fe19fc1
JF
3974}
3975
b4d89997
JF
3976@end
3977/* }}} */
b4d89997 3978/* Package Table {{{ */
36bb2ca2
JF
3979@interface PackageTable : RVPage {
3980 _transient Database *database_;
3981 NSString *title_;
3982 SEL filter_;
3983 id object_;
3984 NSMutableArray *packages_;
b4d89997 3985 NSMutableArray *sections_;
36bb2ca2 3986 UISectionList *list_;
dc5812ec
JF
3987}
3988
36bb2ca2 3989- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
b4d89997
JF
3990
3991- (void) setDelegate:(id)delegate;
a0be02eb
JF
3992- (void) setObject:(id)object;
3993
36bb2ca2 3994- (void) reloadData;
a0be02eb 3995- (void) resetCursor;
b4d89997 3996
2388b078
JF
3997- (UISectionList *) list;
3998
ab398adf
JF
3999- (void) setShouldHideHeaderInShortLists:(BOOL)hide;
4000
b4d89997
JF
4001@end
4002
4003@implementation PackageTable
4004
4005- (void) dealloc {
36bb2ca2
JF
4006 [list_ setDataSource:nil];
4007
4008 [title_ release];
4009 if (object_ != nil)
4010 [object_ release];
4011 [packages_ release];
b4d89997 4012 [sections_ release];
36bb2ca2 4013 [list_ release];
b4d89997 4014 [super dealloc];
8fe19fc1
JF
4015}
4016
b4d89997
JF
4017- (int) numberOfSectionsInSectionList:(UISectionList *)list {
4018 return [sections_ count];
4019}
2367a917 4020
b4d89997
JF
4021- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4022 return [[sections_ objectAtIndex:section] name];
4023}
dc5812ec 4024
b4d89997
JF
4025- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4026 return [[sections_ objectAtIndex:section] row];
2367a917
JF
4027}
4028
b4d89997
JF
4029- (int) numberOfRowsInTable:(UITable *)table {
4030 return [packages_ count];
4031}
dc5812ec 4032
b4d89997 4033- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 4034 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
b4d89997 4035}
dc5812ec 4036
b4d89997
JF
4037- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
4038 if (reusing == nil)
36bb2ca2 4039 reusing = [[[PackageCell alloc] init] autorelease];
b4d89997
JF
4040 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
4041 return reusing;
4042}
dc5812ec 4043
b4d89997
JF
4044- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4045 return NO;
4046}
dc5812ec 4047
b4d89997
JF
4048- (void) tableRowSelected:(NSNotification *)notification {
4049 int row = [[notification object] selectedRow];
36bb2ca2
JF
4050 if (row == INT_MAX)
4051 return;
4052
4053 Package *package = [packages_ objectAtIndex:row];
4054 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
4055 [view setDelegate:delegate_];
4056 [view setPackage:package];
4057 [book_ pushPage:view];
dc5812ec
JF
4058}
4059
36bb2ca2
JF
4060- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
4061 if ((self = [super initWithBook:book]) != nil) {
4062 database_ = database;
4063 title_ = [title retain];
4064 filter_ = filter;
4065 object_ = object == nil ? nil : [object retain];
4066
4067 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
b4d89997 4068 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 4069
b4d89997
JF
4070 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
4071 [list_ setDataSource:self];
dc5812ec 4072
b4d89997
JF
4073 UITableColumn *column = [[[UITableColumn alloc]
4074 initWithTitle:@"Name"
4075 identifier:@"name"
36bb2ca2 4076 width:[self frame].size.width
b4d89997
JF
4077 ] autorelease];
4078
4079 UITable *table = [list_ table];
4080 [table setSeparatorStyle:1];
4081 [table addTableColumn:column];
4082 [table setDelegate:self];
4083 [table setReusesTableCells:YES];
4084
4085 [self addSubview:list_];
36bb2ca2 4086 [self reloadData];
baf80942
JF
4087
4088 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4089 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
b4d89997 4090 } return self;
dc5812ec
JF
4091}
4092
4093- (void) setDelegate:(id)delegate {
2367a917 4094 delegate_ = delegate;
b4d89997
JF
4095}
4096
a0be02eb
JF
4097- (void) setObject:(id)object {
4098 if (object_ != nil)
4099 [object_ release];
4100 if (object == nil)
4101 object_ = nil;
4102 else
4103 object_ = [object retain];
4104}
4105
36bb2ca2
JF
4106- (void) reloadData {
4107 NSArray *packages = [database_ packages];
b4d89997 4108
36bb2ca2 4109 [packages_ removeAllObjects];
b4d89997
JF
4110 [sections_ removeAllObjects];
4111
36bb2ca2
JF
4112 for (size_t i(0); i != [packages count]; ++i) {
4113 Package *package([packages objectAtIndex:i]);
7d2ac47f 4114 if ([package valid] && [[package performSelector:filter_ withObject:object_] boolValue])
36bb2ca2
JF
4115 [packages_ addObject:package];
4116 }
4117
b4d89997
JF
4118 Section *section = nil;
4119
4120 for (size_t offset(0); offset != [packages_ count]; ++offset) {
4121 Package *package = [packages_ objectAtIndex:offset];
4122 NSString *name = [package index];
4123
36bb2ca2 4124 if (section == nil || ![[section name] isEqualToString:name]) {
b4d89997
JF
4125 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4126 [sections_ addObject:section];
4127 }
4128
36bb2ca2 4129 [section addToCount];
b4d89997
JF
4130 }
4131
4132 [list_ reloadData];
4133}
4134
36bb2ca2
JF
4135- (NSString *) title {
4136 return title_;
b4d89997
JF
4137}
4138
36bb2ca2
JF
4139- (void) resetViewAnimated:(BOOL)animated {
4140 [list_ resetViewAnimated:animated];
dc5812ec
JF
4141}
4142
a0be02eb
JF
4143- (void) resetCursor {
4144 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
4145}
4146
2388b078
JF
4147- (UISectionList *) list {
4148 return list_;
4149}
4150
ab398adf
JF
4151- (void) setShouldHideHeaderInShortLists:(BOOL)hide {
4152 [list_ setShouldHideHeaderInShortLists:hide];
4153}
4154
dc5812ec 4155@end
b4d89997 4156/* }}} */
dc5812ec 4157
7b0ce2da
JF
4158/* Add Source View {{{ */
4159@interface AddSourceView : RVPage {
4160 _transient Database *database_;
dc5812ec
JF
4161}
4162
7b0ce2da 4163- (id) initWithBook:(RVBook *)book database:(Database *)database;
b4d89997 4164
7b0ce2da 4165@end
dc5812ec 4166
7b0ce2da 4167@implementation AddSourceView
dc5812ec 4168
7b0ce2da
JF
4169- (id) initWithBook:(RVBook *)book database:(Database *)database {
4170 if ((self = [super initWithBook:book]) != nil) {
4171 database_ = database;
4172 } return self;
36bb2ca2 4173}
dc5812ec 4174
7b0ce2da
JF
4175@end
4176/* }}} */
4177/* Source Cell {{{ */
4178@interface SourceCell : UITableCell {
f641a0e5
JF
4179 UIImage *icon_;
4180 NSString *origin_;
4181 NSString *description_;
4182 NSString *label_;
b4d89997
JF
4183}
4184
7b0ce2da 4185- (void) dealloc;
b4d89997 4186
7b0ce2da 4187- (SourceCell *) initWithSource:(Source *)source;
b4d89997 4188
7b0ce2da 4189@end
b4d89997 4190
7b0ce2da 4191@implementation SourceCell
b4d89997 4192
7b0ce2da 4193- (void) dealloc {
f641a0e5 4194 [icon_ release];
7b0ce2da
JF
4195 [origin_ release];
4196 [description_ release];
4197 [label_ release];
4198 [super dealloc];
36bb2ca2 4199}
b4d89997 4200
7b0ce2da
JF
4201- (SourceCell *) initWithSource:(Source *)source {
4202 if ((self = [super init]) != nil) {
f641a0e5
JF
4203 if (icon_ == nil)
4204 icon_ = [UIImage applicationImageNamed:[NSString stringWithFormat:@"Sources/%@.png", [source host]]];
4205 if (icon_ == nil)
4206 icon_ = [UIImage applicationImageNamed:@"unknown.png"];
4207 icon_ = [icon_ retain];
4208
4209 origin_ = [[source name] retain];
4210 label_ = [[source uri] retain];
4211 description_ = [[source description] retain];
7b0ce2da 4212 } return self;
a0376fc1
JF
4213}
4214
f641a0e5
JF
4215- (void) drawContentInRect:(CGRect)rect selected:(BOOL)selected {
4216 if (icon_ != nil)
4217 [icon_ drawInRect:CGRectMake(10, 10, 30, 30)];
7b0ce2da 4218
f641a0e5
JF
4219 if (selected)
4220 UISetColor(White_);
7b0ce2da 4221
f641a0e5
JF
4222 if (!selected)
4223 UISetColor(Black_);
4224 [origin_ drawAtPoint:CGPointMake(48, 8) forWidth:240 withFont:Font18Bold_ ellipsis:2];
7b0ce2da 4225
f641a0e5
JF
4226 if (!selected)
4227 UISetColor(Blue_);
4228 [label_ drawAtPoint:CGPointMake(58, 29) forWidth:225 withFont:Font12_ ellipsis:2];
7b0ce2da 4229
f641a0e5
JF
4230 if (!selected)
4231 UISetColor(Gray_);
4232 [description_ drawAtPoint:CGPointMake(12, 46) forWidth:280 withFont:Font14_ ellipsis:2];
7b0ce2da 4233
f641a0e5 4234 [super drawContentInRect:rect selected:selected];
7b0ce2da
JF
4235}
4236
4237@end
4238/* }}} */
4239/* Source Table {{{ */
4240@interface SourceTable : RVPage {
4241 _transient Database *database_;
4242 UISectionList *list_;
4243 NSMutableArray *sources_;
a9543575 4244 UIActionSheet *alert_;
7b0ce2da
JF
4245 int offset_;
4246
4247 NSString *href_;
4248 UIProgressHUD *hud_;
4249 NSError *error_;
4250
4251 //NSURLConnection *installer_;
4252 NSURLConnection *trivial_bz2_;
4253 NSURLConnection *trivial_gz_;
4254 //NSURLConnection *automatic_;
4255
4256 BOOL trivial_;
4257}
4258
4259- (id) initWithBook:(RVBook *)book database:(Database *)database;
4260
4261@end
4262
4263@implementation SourceTable
4264
4265- (void) _deallocConnection:(NSURLConnection *)connection {
4266 if (connection != nil) {
4267 [connection cancel];
4268 //[connection setDelegate:nil];
4269 [connection release];
4270 }
4271}
4272
4273- (void) dealloc {
4274 [[list_ table] setDelegate:nil];
4275 [list_ setDataSource:nil];
4276
4277 if (href_ != nil)
4278 [href_ release];
4279 if (hud_ != nil)
4280 [hud_ release];
4281 if (error_ != nil)
4282 [error_ release];
4283
4284 //[self _deallocConnection:installer_];
4285 [self _deallocConnection:trivial_gz_];
4286 [self _deallocConnection:trivial_bz2_];
4287 //[self _deallocConnection:automatic_];
4288
4289 [sources_ release];
4290 [list_ release];
4291 [super dealloc];
4292}
4293
4294- (int) numberOfSectionsInSectionList:(UISectionList *)list {
4295 return offset_ == 0 ? 1 : 2;
4296}
4297
4298- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
4299 switch (section + (offset_ == 0 ? 1 : 0)) {
4300 case 0: return @"Entered by User";
4301 case 1: return @"Installed by Packages";
4302
4303 default:
4304 _assert(false);
4305 return nil;
4306 }
4307}
4308
4309- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
4310 switch (section + (offset_ == 0 ? 1 : 0)) {
4311 case 0: return 0;
4312 case 1: return offset_;
4313
4314 default:
4315 _assert(false);
4316 return -1;
4317 }
4318}
4319
4320- (int) numberOfRowsInTable:(UITable *)table {
4321 return [sources_ count];
4322}
4323
4324- (float) table:(UITable *)table heightForRow:(int)row {
4325 Source *source = [sources_ objectAtIndex:row];
f641a0e5 4326 return [source description] == nil ? 56 : 73;
7b0ce2da
JF
4327}
4328
4329- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col {
4330 Source *source = [sources_ objectAtIndex:row];
4331 // XXX: weird warning, stupid selectors ;P
4332 return [[[SourceCell alloc] initWithSource:(id)source] autorelease];
4333}
4334
4335- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
4336 return YES;
4337}
4338
4339- (BOOL) table:(UITable *)table canSelectRow:(int)row {
4340 return YES;
4341}
4342
4343- (void) tableRowSelected:(NSNotification*)notification {
4344 UITable *table([list_ table]);
4345 int row([table selectedRow]);
4346 if (row == INT_MAX)
4347 return;
4348
4349 Source *source = [sources_ objectAtIndex:row];
4350
4351 PackageTable *packages = [[[PackageTable alloc]
4352 initWithBook:book_
4353 database:database_
4354 title:[source label]
4355 filter:@selector(isVisibleInSource:)
4356 with:source
4357 ] autorelease];
4358
4359 [packages setDelegate:delegate_];
4360
4361 [book_ pushPage:packages];
4362}
4363
4364- (BOOL) table:(UITable *)table canDeleteRow:(int)row {
4365 Source *source = [sources_ objectAtIndex:row];
4366 return [source record] != nil;
4367}
4368
4369- (void) table:(UITable *)table willSwipeToDeleteRow:(int)row {
4370 [[list_ table] setDeleteConfirmationRow:row];
4371}
4372
4373- (void) table:(UITable *)table deleteRow:(int)row {
4374 Source *source = [sources_ objectAtIndex:row];
4375 [Sources_ removeObjectForKey:[source key]];
4376 [delegate_ syncData];
4377}
4378
4379- (void) _endConnection:(NSURLConnection *)connection {
4380 NSURLConnection **field = NULL;
4381 if (connection == trivial_bz2_)
4382 field = &trivial_bz2_;
4383 else if (connection == trivial_gz_)
4384 field = &trivial_gz_;
4385 _assert(field != NULL);
4386 [connection release];
4387 *field = nil;
4388
4389 if (
4390 trivial_bz2_ == nil &&
4391 trivial_gz_ == nil
4392 ) {
4393 [delegate_ setStatusBarShowsProgress:NO];
4394
4395 [hud_ show:NO];
4396 [hud_ removeFromSuperview];
4397 [hud_ autorelease];
4398 hud_ = nil;
4399
4400 if (trivial_) {
4401 [Sources_ setObject:[NSDictionary dictionaryWithObjectsAndKeys:
4402 @"deb", @"Type",
4403 href_, @"URI",
4404 @"./", @"Distribution",
4405 nil] forKey:[NSString stringWithFormat:@"deb:%@:./", href_]];
4406
4407 [delegate_ syncData];
4408 } else if (error_ != nil) {
a9543575 4409 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4410 initWithTitle:@"Verification Error"
4411 buttons:[NSArray arrayWithObjects:@"OK", nil]
4412 defaultButtonIndex:0
4413 delegate:self
4414 context:@"urlerror"
4415 ] autorelease];
4416
4417 [sheet setBodyText:[error_ localizedDescription]];
4418 [sheet popupAlertAnimated:YES];
4419 } else {
a9543575 4420 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4421 initWithTitle:@"Did not Find Repository"
4422 buttons:[NSArray arrayWithObjects:@"OK", nil]
4423 defaultButtonIndex:0
4424 delegate:self
4425 context:@"trivial"
4426 ] autorelease];
4427
4428 [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."];
4429 [sheet popupAlertAnimated:YES];
4430 }
4431
4432 [href_ release];
4433 href_ = nil;
4434
4435 if (error_ != nil) {
4436 [error_ release];
4437 error_ = nil;
4438 }
4439 }
4440}
4441
4442- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
4443 switch ([response statusCode]) {
4444 case 200:
4445 trivial_ = YES;
4446 }
4447}
4448
4449- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
c390d3ab 4450 lprintf("connection:\"%s\" didFailWithError:\"%s\"", [href_ UTF8String], [[error localizedDescription] UTF8String]);
7b0ce2da
JF
4451 if (error_ != nil)
4452 error_ = [error retain];
4453 [self _endConnection:connection];
4454}
4455
4456- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
4457 [self _endConnection:connection];
4458}
4459
4460- (NSURLConnection *) _requestHRef:(NSString *)href method:(NSString *)method {
4461 NSMutableURLRequest *request = [NSMutableURLRequest
4462 requestWithURL:[NSURL URLWithString:href]
4463 cachePolicy:NSURLRequestUseProtocolCachePolicy
4464 timeoutInterval:20.0
4465 ];
4466
4467 [request setHTTPMethod:method];
4468
4469 return [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
4470}
4471
a9543575 4472- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821
JF
4473 NSString *context([sheet context]);
4474
4475 if ([context isEqualToString:@"source"]) {
7b0ce2da
JF
4476 switch (button) {
4477 case 1: {
4478 NSString *href = [[sheet textField] text];
4479
4480 //installer_ = [[self _requestHRef:href method:@"GET"] retain];
4481
4482 if (![href hasSuffix:@"/"])
4483 href_ = [href stringByAppendingString:@"/"];
4484 else
4485 href_ = href;
4486 href_ = [href_ retain];
4487
4488 trivial_bz2_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.bz2"] method:@"HEAD"] retain];
4489 trivial_gz_ = [[self _requestHRef:[href_ stringByAppendingString:@"Packages.gz"] method:@"HEAD"] retain];
4490 //trivial_bz2_ = [[self _requestHRef:[href stringByAppendingString:@"dists/Release"] method:@"HEAD"] retain];
4491
4492 trivial_ = false;
4493
4494 hud_ = [delegate_ addProgressHUD];
4495 [hud_ setText:@"Verifying URL"];
4496 } break;
4497
4498 case 2:
4499 break;
4500
4501 default:
4502 _assert(false);
4503 }
4504
1cedb821 4505 [sheet dismiss];
b49f4c92
JF
4506 } else if ([context isEqualToString:@"trivial"])
4507 [sheet dismiss];
4508 else if ([context isEqualToString:@"urlerror"])
4509 [sheet dismiss];
7b0ce2da
JF
4510}
4511
4512- (id) initWithBook:(RVBook *)book database:(Database *)database {
4513 if ((self = [super initWithBook:book]) != nil) {
4514 database_ = database;
4515 sources_ = [[NSMutableArray arrayWithCapacity:16] retain];
4516
4517 //list_ = [[UITable alloc] initWithFrame:[self bounds]];
4518 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4519 [list_ setShouldHideHeaderInShortLists:NO];
4520
4521 [self addSubview:list_];
4522 [list_ setDataSource:self];
4523
4524 UITableColumn *column = [[UITableColumn alloc]
4525 initWithTitle:@"Name"
4526 identifier:@"name"
4527 width:[self frame].size.width
4528 ];
4529
4530 UITable *table = [list_ table];
4531 [table setSeparatorStyle:1];
4532 [table addTableColumn:column];
4533 [table setDelegate:self];
4534
4535 [self reloadData];
baf80942
JF
4536
4537 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4538 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
7b0ce2da
JF
4539 } return self;
4540}
4541
4542- (void) reloadData {
4543 pkgSourceList list;
4544 _assert(list.ReadMainList());
4545
4546 [sources_ removeAllObjects];
4547 [sources_ addObjectsFromArray:[database_ sources]];
807ae6d7 4548 _trace();
7b0ce2da 4549 [sources_ sortUsingSelector:@selector(compareByNameAndType:)];
807ae6d7 4550 _trace();
7b0ce2da
JF
4551
4552 int count = [sources_ count];
4553 for (offset_ = 0; offset_ != count; ++offset_) {
4554 Source *source = [sources_ objectAtIndex:offset_];
4555 if ([source record] == nil)
4556 break;
4557 }
4558
4559 [list_ reloadData];
4560}
4561
4562- (void) resetViewAnimated:(BOOL)animated {
4563 [list_ resetViewAnimated:animated];
4564}
4565
4566- (void) _leftButtonClicked {
4567 /*[book_ pushPage:[[[AddSourceView alloc]
4568 initWithBook:book_
4569 database:database_
4570 ] autorelease]];*/
4571
a9543575 4572 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4573 initWithTitle:@"Enter Cydia/APT URL"
4574 buttons:[NSArray arrayWithObjects:@"Add Source", @"Cancel", nil]
4575 defaultButtonIndex:0
4576 delegate:self
4577 context:@"source"
4578 ] autorelease];
4579
1cedb821
JF
4580 [sheet setNumberOfRows:1];
4581
7b0ce2da
JF
4582 [sheet addTextFieldWithValue:@"http://" label:@""];
4583
a9543575 4584 UITextInputTraits *traits = [[sheet textField] textInputTraits];
b49f4c92
JF
4585 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
4586 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
1cedb821 4587 [traits setKeyboardType:UIKeyboardTypeURL];
b49f4c92
JF
4588 // XXX: UIReturnKeyDone
4589 [traits setReturnKeyType:UIReturnKeyNext];
7b0ce2da
JF
4590
4591 [sheet popupAlertAnimated:YES];
4592}
4593
4594- (void) _rightButtonClicked {
4595 UITable *table = [list_ table];
4596 BOOL editing = [table isRowDeletionEnabled];
4597 [table enableRowDeletion:!editing animated:YES];
4598 [book_ reloadButtonsForPage:self];
4599}
4600
4601- (NSString *) title {
4602 return @"Sources";
4603}
4604
7b0ce2da
JF
4605- (NSString *) leftButtonTitle {
4606 return [[list_ table] isRowDeletionEnabled] ? @"Add" : nil;
4607}
4608
fd7853a6 4609- (id) rightButtonTitle {
7b0ce2da
JF
4610 return [[list_ table] isRowDeletionEnabled] ? @"Done" : @"Edit";
4611}
4612
baf80942
JF
4613- (UINavigationButtonStyle) rightButtonStyle {
4614 return [[list_ table] isRowDeletionEnabled] ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
7b0ce2da
JF
4615}
4616
4617@end
4618/* }}} */
4619
4620/* Installed View {{{ */
4621@interface InstalledView : RVPage {
4622 _transient Database *database_;
4623 PackageTable *packages_;
f641a0e5 4624 BOOL expert_;
7b0ce2da
JF
4625}
4626
4627- (id) initWithBook:(RVBook *)book database:(Database *)database;
4628
4629@end
4630
4631@implementation InstalledView
4632
4633- (void) dealloc {
4634 [packages_ release];
4635 [super dealloc];
4636}
4637
4638- (id) initWithBook:(RVBook *)book database:(Database *)database {
4639 if ((self = [super initWithBook:book]) != nil) {
4640 database_ = database;
4641
4642 packages_ = [[PackageTable alloc]
4643 initWithBook:book
4644 database:database
4645 title:nil
440c215d 4646 filter:@selector(isInstalledAndVisible:)
f641a0e5 4647 with:[NSNumber numberWithBool:YES]
7b0ce2da
JF
4648 ];
4649
4650 [self addSubview:packages_];
baf80942
JF
4651
4652 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
4653 [packages_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
7b0ce2da
JF
4654 } return self;
4655}
4656
4657- (void) resetViewAnimated:(BOOL)animated {
4658 [packages_ resetViewAnimated:animated];
4659}
4660
4661- (void) reloadData {
4662 [packages_ reloadData];
4663}
4664
f641a0e5
JF
4665- (void) _rightButtonClicked {
4666 [packages_ setObject:[NSNumber numberWithBool:expert_]];
4667 [packages_ reloadData];
4668 expert_ = !expert_;
4669 [book_ reloadButtonsForPage:self];
4670}
4671
7b0ce2da 4672- (NSString *) title {
f641a0e5 4673 return @"Installed";
7b0ce2da
JF
4674}
4675
4676- (NSString *) backButtonTitle {
4677 return @"Packages";
4678}
4679
fd7853a6 4680- (id) rightButtonTitle {
f641a0e5
JF
4681 return Role_ != nil && [Role_ isEqualToString:@"Developer"] ? nil : expert_ ? @"Expert" : @"Simple";
4682}
4683
baf80942
JF
4684- (UINavigationButtonStyle) rightButtonStyle {
4685 return expert_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
f641a0e5
JF
4686}
4687
7b0ce2da
JF
4688- (void) setDelegate:(id)delegate {
4689 [super setDelegate:delegate];
4690 [packages_ setDelegate:delegate];
4691}
4692
4693@end
4694/* }}} */
4695
98228790 4696/* Home View {{{ */
7b0ce2da
JF
4697@interface HomeView : BrowserView {
4698}
4699
4700@end
4701
4702@implementation HomeView
4703
a9543575 4704- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821
JF
4705 NSString *context([sheet context]);
4706
4707 if ([context isEqualToString:@"about"])
4708 [sheet dismiss];
4709 else
4710 [super alertSheet:sheet buttonClicked:button];
7b0ce2da
JF
4711}
4712
4713- (void) _leftButtonClicked {
a9543575 4714 UIActionSheet *sheet = [[[UIActionSheet alloc]
7b0ce2da
JF
4715 initWithTitle:@"About Cydia Installer"
4716 buttons:[NSArray arrayWithObjects:@"Close", nil]
4717 defaultButtonIndex:0
4718 delegate:self
4719 context:@"about"
4720 ] autorelease];
4721
4722 [sheet setBodyText:
4723 @"Copyright (C) 2008\n"
4724 "Jay Freeman (saurik)\n"
4725 "saurik@saurik.com\n"
4726 "http://www.saurik.com/\n"
4727 "\n"
4728 "The Okori Group\n"
4729 "http://www.theokorigroup.com/\n"
4730 "\n"
4731 "College of Creative Studies,\n"
4732 "University of California,\n"
4733 "Santa Barbara\n"
4734 "http://www.ccs.ucsb.edu/"
4735 ];
4736
4737 [sheet popupAlertAnimated:YES];
4738}
4739
4740- (NSString *) leftButtonTitle {
4741 return @"About";
4742}
4743
4744@end
98228790
JF
4745/* }}} */
4746/* Manage View {{{ */
7b0ce2da
JF
4747@interface ManageView : BrowserView {
4748}
4749
4750@end
4751
4752@implementation ManageView
4753
4754- (NSString *) title {
4755 return @"Manage";
4756}
4757
4758- (void) _leftButtonClicked {
4759 [delegate_ askForSettings];
4760}
4761
4762- (NSString *) leftButtonTitle {
4763 return @"Settings";
4764}
4765
bf8476c8 4766#if !AlwaysReload
fd7853a6 4767- (id) _rightButtonTitle {
7b0ce2da
JF
4768 return nil;
4769}
bf8476c8 4770#endif
7b0ce2da 4771
9fe5e5f8
JF
4772- (bool) _loading {
4773 return false;
4774}
4775
7b0ce2da 4776@end
98228790 4777/* }}} */
7b0ce2da 4778
c390d3ab
JF
4779@interface WebView (Cydia)
4780- (void) setScriptDebugDelegate:(id)delegate;
4781- (void) _setFormDelegate:(id)delegate;
4782- (void) _setUIKitDelegate:(id)delegate;
4783- (void) setWebMailDelegate:(id)delegate;
4784- (void) _setLayoutInterval:(float)interval;
4785@end
4786
baf80942
JF
4787/* Indirect Delegate {{{ */
4788@interface IndirectDelegate : NSProxy {
c390d3ab 4789 _transient volatile id delegate_;
baf80942
JF
4790}
4791
4792- (void) setDelegate:(id)delegate;
4793- (id) initWithDelegate:(id)delegate;
4794@end
4795
4796@implementation IndirectDelegate
4797
4798- (void) setDelegate:(id)delegate {
4799 delegate_ = delegate;
4800}
4801
4802- (id) initWithDelegate:(id)delegate {
4803 delegate_ = delegate;
4804 return self;
4805}
4806
4807- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
4808 if (delegate_ != nil)
4809 if (NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel])
4810 return sig;
c390d3ab
JF
4811 // XXX: I fucking hate Apple so very very bad
4812 return [NSMethodSignature signatureWithObjCTypes:"v@:"];
baf80942
JF
4813}
4814
c390d3ab 4815- (void) forwardInvocation:(NSInvocation *)inv {
baf80942
JF
4816 SEL sel = [inv selector];
4817 if (delegate_ != nil && [delegate_ respondsToSelector:sel])
4818 [inv invokeWithTarget:delegate_];
4819}
4820
4821@end
4822/* }}} */
7b0ce2da
JF
4823/* Browser Implementation {{{ */
4824@implementation BrowserView
0c7c9052
JF
4825
4826#if ForSaurik
680eb135 4827#include "internals.h"
0c7c9052 4828#endif
7b0ce2da
JF
4829
4830- (void) dealloc {
1cedb821
JF
4831 if (challenge_ != nil)
4832 [challenge_ release];
4833
7b0ce2da
JF
4834 WebView *webview = [webview_ webView];
4835 [webview setFrameLoadDelegate:nil];
4836 [webview setResourceLoadDelegate:nil];
4837 [webview setUIDelegate:nil];
c390d3ab
JF
4838 [webview setScriptDebugDelegate:nil];
4839 [webview setPolicyDelegate:nil];
4840
4841 [webview setDownloadDelegate:nil];
4842
4843 [webview _setFormDelegate:nil];
4844 [webview _setUIKitDelegate:nil];
4845 [webview setWebMailDelegate:nil];
4846 [webview setEditingDelegate:nil];
7b0ce2da 4847
7b0ce2da 4848 [webview_ setDelegate:nil];
baf80942 4849 [webview_ setGestureDelegate:nil];
7b0ce2da 4850
c390d3ab
JF
4851 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
4852
c390d3ab
JF
4853 [webview close];
4854
affeffc7
JF
4855#if RecycleWebViews
4856 [webview_ removeFromSuperview];
4857 [Documents_ addObject:[webview_ autorelease]];
4858#else
7b0ce2da 4859 [webview_ release];
affeffc7 4860#endif
baf80942
JF
4861
4862 [indirect_ setDelegate:nil];
4863 [indirect_ release];
4864
4865 [scroller_ setDelegate:nil];
4866
9fe5e5f8
JF
4867 if (button_ != nil)
4868 [button_ release];
4869 if (style_ != nil)
4870 [style_ release];
4871 if (function_ != nil)
4872 [function_ release];
4873
baf80942 4874 [scroller_ release];
7b0ce2da 4875 [indicator_ release];
68f1828e
JF
4876 if (confirm_ != nil)
4877 [confirm_ release];
7b0ce2da
JF
4878 if (title_ != nil)
4879 [title_ release];
4880 [super dealloc];
4881}
4882
4883- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
4884 [self loadRequest:[NSURLRequest
4885 requestWithURL:url
4886 cachePolicy:policy
4887 timeoutInterval:30.0
4888 ]];
4889}
4890
4891- (void) loadURL:(NSURL *)url {
4892 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
4893}
4894
0c7c9052 4895- (NSMutableURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
7b0ce2da
JF
4896 NSMutableURLRequest *copy = [request mutableCopy];
4897
c390d3ab 4898 if (Machine_ != NULL)
0c7c9052 4899 [copy setValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
c390d3ab 4900 if (UniqueID_ != nil)
0c7c9052 4901 [copy setValue:UniqueID_ forHTTPHeaderField:@"X-Unique-ID"];
7b0ce2da
JF
4902
4903 if (Role_ != nil)
0c7c9052 4904 [copy setValue:Role_ forHTTPHeaderField:@"X-Role"];
7b0ce2da
JF
4905
4906 return copy;
4907}
4908
4909- (void) loadRequest:(NSURLRequest *)request {
4910 pushed_ = true;
4911 [webview_ loadRequest:request];
4912}
4913
4914- (void) reloadURL {
0c7c9052
JF
4915 NSLog(@"rlu:%@", request_);
4916 if (request_ == nil)
87f46a96 4917 return;
0c7c9052
JF
4918
4919 if ([request_ HTTPBody] == nil && [request_ HTTPBodyStream] == nil)
4920 [webview_ loadRequest:request_];
4921 else {
4922 UIActionSheet *sheet = [[[UIActionSheet alloc]
4923 initWithTitle:@"Are you sure you want to submit this form again?"
4924 buttons:[NSArray arrayWithObjects:@"Cancel", @"Submit", nil]
4925 defaultButtonIndex:0
4926 delegate:self
4927 context:@"submit"
4928 ] autorelease];
4929
4930 [sheet setNumberOfRows:1];
4931 [sheet popupAlertAnimated:YES];
4932 }
7b0ce2da
JF
4933}
4934
4935- (WebView *) webView {
4936 return [webview_ webView];
4937}
4938
4939- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
4940 [scroller_ setContentSize:frame.size];
4941}
4942
4943- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
4944 [self view:sender didSetFrame:frame];
4945}
4946
4947- (void) pushPage:(RVPage *)page {
4948 [self setBackButtonTitle:title_];
4949 [page setDelegate:delegate_];
4950 [book_ pushPage:page];
4951}
4952
affeffc7
JF
4953- (BOOL) getSpecial:(NSURL *)url {
4954 NSString *href([url absoluteString]);
4955 NSString *scheme([[url scheme] lowercaseString]);
4956
7b0ce2da
JF
4957 RVPage *page = nil;
4958
affeffc7 4959 if ([href hasPrefix:@"apptapp://package/"])
c390d3ab 4960 page = [delegate_ pageForPackage:[href substringFromIndex:18]];
770f2a8e 4961 else if ([scheme isEqualToString:@"cydia"]) {
affeffc7 4962 page = [delegate_ pageForURL:url hasTag:NULL];
770f2a8e
JF
4963 if (page == nil)
4964 return false;
4965 } else if (![scheme isEqualToString:@"apptapp"])
a9a0661e 4966 return false;
f641a0e5
JF
4967
4968 if (page != nil)
4969 [self pushPage:page];
a9a0661e 4970 return true;
7b0ce2da
JF
4971}
4972
ff0210e5
JF
4973- (void) webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4974 UIActionSheet *sheet = [[[UIActionSheet alloc]
6565453a 4975 initWithTitle:nil
ff0210e5
JF
4976 buttons:[NSArray arrayWithObjects:@"OK", nil]
4977 defaultButtonIndex:0
4978 delegate:self
4979 context:@"alert"
4980 ] autorelease];
4981
4982 [sheet setBodyText:message];
4983 [sheet popupAlertAnimated:YES];
4984}
4985
68f1828e
JF
4986- (BOOL) webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame {
4987 UIActionSheet *sheet = [[[UIActionSheet alloc]
6565453a 4988 initWithTitle:nil
68f1828e
JF
4989 buttons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil]
4990 defaultButtonIndex:0
4991 delegate:self
4992 context:@"confirm"
4993 ] autorelease];
4994
4995 [sheet setNumberOfRows:1];
4996 [sheet setBodyText:message];
4997 [sheet popupAlertAnimated:YES];
4998
4999 NSRunLoop *loop([NSRunLoop currentRunLoop]);
5000 NSDate *future([NSDate distantFuture]);
5001
5002 while (confirm_ == nil && [loop runMode:NSDefaultRunLoopMode beforeDate:future]);
5003
5004 NSNumber *confirm([confirm_ autorelease]);
5005 confirm_ = nil;
5006 return [confirm boolValue];
5007}
5008
9fe5e5f8
JF
5009/* Web Scripting {{{ */
5010+ (NSString *) webScriptNameForSelector:(SEL)selector {
5011 if (selector == @selector(getPackageById:))
5012 return @"getPackageById";
fd7853a6
JF
5013 else if (selector == @selector(setButtonImage:withStyle:toFunction:))
5014 return @"setButtonImage";
6565453a
JF
5015 else if (selector == @selector(setButtonTitle:withStyle:toFunction:))
5016 return @"setButtonTitle";
9fe5e5f8
JF
5017 else if (selector == @selector(supports:))
5018 return @"supports";
5019 else
5020 return nil;
5021}
5022
5023+ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector {
5024 return [self webScriptNameForSelector:selector] == nil;
5025}
5026
5027- (BOOL) supports:(NSString *)feature {
5028 return [feature isEqualToString:@"window.open"];
5029}
5030
5031- (Package *) getPackageById:(NSString *)id {
5032 return [[Database sharedInstance] packageWithName:id];
5033}
5034
fd7853a6
JF
5035- (void) setButtonImage:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
5036 if (button_ != nil)
5037 [button_ autorelease];
5038 button_ = button == nil ? nil : [[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:button]]] retain];
5039
5040 if (style_ != nil)
5041 [style_ autorelease];
5042 style_ = style == nil ? nil : [style retain];
5043
5044 if (function_ != nil)
5045 [function_ autorelease];
5046 function_ = function == nil ? nil : [function retain];
5047}
5048
6565453a 5049- (void) setButtonTitle:(NSString *)button withStyle:(NSString *)style toFunction:(id)function {
9fe5e5f8
JF
5050 if (button_ != nil)
5051 [button_ autorelease];
5052 button_ = button == nil ? nil : [button retain];
5053
5054 if (style_ != nil)
5055 [style_ autorelease];
5056 style_ = style == nil ? nil : [style retain];
5057
5058 if (function_ != nil)
5059 [function_ autorelease];
5060 function_ = function == nil ? nil : [function retain];
5061}
5062/* }}} */
5063
c390d3ab 5064- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
9fe5e5f8 5065 [window setValue:self forKey:@"cydia"];
c390d3ab
JF
5066}
5067
0c7c9052
JF
5068- (void) webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame {
5069 NSLog(@"err:%@", error);
5070}
5071
680eb135 5072- (void) webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)name decisionListener:(id<WebPolicyDecisionListener>)listener {
affeffc7 5073 if (NSURL *url = [request URL]) {
9fe5e5f8
JF
5074 if (name != nil && [name isEqualToString:@"_open"])
5075 [delegate_ openURL:url];
5076
680eb135 5077 NSLog(@"win:%@:%@", url, [action description]);
affeffc7
JF
5078 if (![self getSpecial:url]) {
5079 NSString *scheme([[url scheme] lowercaseString]);
5080 if ([scheme isEqualToString:@"mailto"])
5081 [delegate_ openMailToURL:url];
5082 else goto use;
5083 }
5084
5085 [listener ignore];
5086 } else use:
5087 [listener use];
5088}
5089
807ae6d7
JF
5090- (void) webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
5091 if ([WebView canShowMIMEType:type])
5092 [listener use];
5093 else {
5094 // XXX: handle more mime types!
5095 [listener ignore];
5096 if (frame == [webView mainFrame])
5097 [UIApp openURL:[request URL]];
5098 }
5099}
5100
affeffc7 5101- (void) webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)action request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
0c7c9052
JF
5102 if (request == nil) ignore: {
5103 [listener ignore];
5104 return;
5105 }
5106
affeffc7
JF
5107 NSURL *url([request URL]);
5108
5109 if (url == nil) use: {
0c7c9052
JF
5110 if ([frame parentFrame] == nil) {
5111 if (request_ != nil)
5112 [request_ autorelease];
5113 request_ = [request retain];
5114 NSLog(@"dpn:%@", request_);
5115 }
5116
affeffc7 5117 [listener use];
7b0ce2da 5118 return;
affeffc7 5119 }
0c7c9052 5120#if ForSaurik
680eb135 5121 else NSLog(@"nav:%@:%@", url, [action description]);
0c7c9052 5122#endif
affeffc7
JF
5123
5124 const NSArray *capability(reinterpret_cast<const NSArray *>(GSSystemGetCapability(kGSDisplayIdentifiersCapability)));
5125
5126 if (
5127 [capability containsObject:@"com.apple.Maps"] && [url mapsURL] ||
5128 [capability containsObject:@"com.apple.youtube"] && [url youTubeURL]
5129 ) {
5130 open:
5131 [UIApp openURL:url];
0c7c9052 5132 goto ignore;
affeffc7
JF
5133 }
5134
5135 int store(_not(int));
5136 if (NSURL *itms = [url itmsURL:&store]) {
807ae6d7 5137 NSLog(@"itms#%@#%u#%@", url, store, itms);
affeffc7
JF
5138 if (
5139 store == 1 && [capability containsObject:@"com.apple.MobileStore"] ||
5140 store == 2 && [capability containsObject:@"com.apple.AppStore"]
5141 ) {
5142 url = itms;
5143 goto open;
5144 }
5145 }
5146
5147 NSString *scheme([[url scheme] lowercaseString]);
5148
5149 if ([scheme isEqualToString:@"tel"]) {
5150 // XXX: intelligence
5151 goto open;
5152 }
5153
5154 if ([scheme isEqualToString:@"mailto"]) {
5155 [delegate_ openMailToURL:url];
5156 goto ignore;
5157 }
5158
5159 if ([self getSpecial:url])
5160 goto ignore;
5161 else if ([WebView _canHandleRequest:request])
5162 goto use;
5163 else if ([url isSpringboardHandledURL])
5164 goto open;
5165 else
5166 goto use;
7b0ce2da
JF
5167}
5168
c390d3ab
JF
5169- (void) webView:(WebView *)sender setStatusText:(NSString *)text {
5170 //lprintf("Status:%s\n", [text UTF8String]);
5171}
5172
5173- (void) _pushPage {
5174 if (pushed_)
5175 return;
5176 pushed_ = true;
5177 [book_ pushPage:self];
5178}
5179
1cedb821
JF
5180- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5181 NSString *context([sheet context]);
5182
ff0210e5
JF
5183 if ([context isEqualToString:@"alert"])
5184 [sheet dismiss];
68f1828e
JF
5185 else if ([context isEqualToString:@"confirm"]) {
5186 switch (button) {
5187 case 1:
5188 confirm_ = [NSNumber numberWithBool:YES];
5189 break;
5190
5191 case 2:
5192 confirm_ = [NSNumber numberWithBool:NO];
5193 break;
5194 }
5195
5196 [sheet dismiss];
5197 } else if ([context isEqualToString:@"challenge"]) {
1cedb821
JF
5198 id<NSURLAuthenticationChallengeSender> sender([challenge_ sender]);
5199
5200 switch (button) {
5201 case 1: {
5202 NSString *username([[sheet textFieldAtIndex:0] text]);
5203 NSString *password([[sheet textFieldAtIndex:1] text]);
5204
5205 NSURLCredential *credential([NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession]);
5206
5207 [sender useCredential:credential forAuthenticationChallenge:challenge_];
5208 } break;
5209
5210 case 2:
5211 [sender cancelAuthenticationChallenge:challenge_];
5212 break;
5213
5214 default:
5215 _assert(false);
5216 }
5217
5218 [challenge_ release];
5219 challenge_ = nil;
5220
0c7c9052
JF
5221 [sheet dismiss];
5222 } else if ([context isEqualToString:@"submit"]) {
5223 switch (button) {
5224 case 1:
5225 break;
5226
5227 case 2:
5228 if (request_ != nil)
5229 [webview_ loadRequest:request_];
5230 break;
5231
5232 default:
5233 _assert(false);
5234 }
5235
1cedb821
JF
5236 [sheet dismiss];
5237 }
5238}
5239
5240- (void) webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)source {
5241 challenge_ = [challenge retain];
5242
5243 NSURLProtectionSpace *space([challenge protectionSpace]);
5244 NSString *realm([space realm]);
5245 if (realm == nil)
5246 realm = @"";
5247
5248 UIActionSheet *sheet = [[[UIActionSheet alloc]
5249 initWithTitle:realm
5250 buttons:[NSArray arrayWithObjects:@"Login", @"Cancel", nil]
5251 defaultButtonIndex:0
5252 delegate:self
5253 context:@"challenge"
5254 ] autorelease];
5255
5256 [sheet setNumberOfRows:1];
5257
5258 [sheet addTextFieldWithValue:@"" label:@"username"];
5259 [sheet addTextFieldWithValue:@"" label:@"password"];
5260
5261 UITextField *username([sheet textFieldAtIndex:0]); {
5262 UITextInputTraits *traits([username textInputTraits]);
b49f4c92
JF
5263 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5264 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5265 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5266 [traits setReturnKeyType:UIReturnKeyNext];
1cedb821
JF
5267 }
5268
5269 UITextField *password([sheet textFieldAtIndex:1]); {
5270 UITextInputTraits *traits([password textInputTraits]);
b49f4c92
JF
5271 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
5272 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
5273 [traits setKeyboardType:UIKeyboardTypeASCIICapable];
5274 // XXX: UIReturnKeyDone
5275 [traits setReturnKeyType:UIReturnKeyNext];
cf1b0804 5276 [traits setSecureTextEntry:YES];
1cedb821
JF
5277 }
5278
5279 [sheet popupAlertAnimated:YES];
5280}
5281
5282- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)source {
f641a0e5 5283 NSURL *url = [request URL];
affeffc7 5284 if ([self getSpecial:url])
7b0ce2da 5285 return nil;
c390d3ab 5286 [self _pushPage];
7b0ce2da
JF
5287 return [self _addHeadersToRequest:request];
5288}
5289
c390d3ab 5290- (WebView *) _createWebViewWithRequest:(NSURLRequest *)request pushed:(BOOL)pushed {
7b0ce2da
JF
5291 [self setBackButtonTitle:title_];
5292
770f2a8e 5293 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_] autorelease];
7b0ce2da
JF
5294 [browser setDelegate:delegate_];
5295
c390d3ab 5296 if (pushed) {
0c7c9052 5297 [browser loadRequest:request];
7b0ce2da
JF
5298 [book_ pushPage:browser];
5299 }
5300
5301 return [browser webView];
5302}
5303
c390d3ab
JF
5304- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
5305 return [self _createWebViewWithRequest:request pushed:(request != nil)];
5306}
5307
5308- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features {
5309 return [self _createWebViewWithRequest:request pushed:YES];
5310}
5311
7b0ce2da 5312- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
25a2158d
JF
5313 if ([frame parentFrame] != nil)
5314 return;
5315
7b0ce2da 5316 title_ = [title retain];
affeffc7 5317 [book_ reloadTitleForPage:self];
7b0ce2da 5318}
36bb2ca2
JF
5319
5320- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
5321 if ([frame parentFrame] != nil)
5322 return;
5323
1d3b229d 5324 reloading_ = false;
36bb2ca2 5325 loading_ = true;
36bb2ca2
JF
5326 [self reloadButtons];
5327
5328 if (title_ != nil) {
5329 [title_ release];
5330 title_ = nil;
b4d89997 5331 }
36bb2ca2 5332
9fe5e5f8
JF
5333 if (button_ != nil) {
5334 [button_ release];
5335 button_ = nil;
5336 }
5337
5338 if (style_ != nil) {
5339 [style_ release];
5340 style_ = nil;
5341 }
5342
5343 if (function_ != nil) {
5344 [function_ release];
5345 function_ = nil;
5346 }
5347
affeffc7 5348 [book_ reloadTitleForPage:self];
36bb2ca2 5349
c390d3ab
JF
5350 [scroller_ scrollPointVisibleAtTopLeft:CGPointZero];
5351
7d2ac47f 5352 CGRect webrect = [scroller_ bounds];
36bb2ca2
JF
5353 webrect.size.height = 0;
5354 [webview_ setFrame:webrect];
dc5812ec
JF
5355}
5356
36bb2ca2 5357- (void) _finishLoading {
1d3b229d
JF
5358 if (!reloading_) {
5359 loading_ = false;
1d3b229d
JF
5360 [self reloadButtons];
5361 }
36bb2ca2 5362}
b4d89997 5363
9fe5e5f8
JF
5364- (bool) _loading {
5365 return loading_;
5366}
5367
5368- (void) reloadButtons {
5369 if ([self _loading])
5370 [indicator_ startAnimation];
5371 else
5372 [indicator_ stopAnimation];
5373 [super reloadButtons];
5374}
5375
7d2ac47f 5376- (BOOL) webView:(WebView *)sender shouldScrollToPoint:(struct CGPoint)point forFrame:(WebFrame *)frame {
7d2ac47f
JF
5377 return [webview_ webView:sender shouldScrollToPoint:point forFrame:frame];
5378}
5379
5380- (void) webView:(WebView *)sender didReceiveViewportArguments:(id)arguments forFrame:(WebFrame *)frame {
5381 return [webview_ webView:sender didReceiveViewportArguments:arguments forFrame:frame];
5382}
5383
5384- (void) webView:(WebView *)sender needsScrollNotifications:(id)notifications forFrame:(WebFrame *)frame {
5385 return [webview_ webView:sender needsScrollNotifications:notifications forFrame:frame];
5386}
5387
5388- (void) webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame {
5389 return [webview_ webView:sender didCommitLoadForFrame:frame];
5390}
5391
5392- (void) webView:(WebView *)sender didReceiveDocTypeForFrame:(WebFrame *)frame {
5393 return [webview_ webView:sender didReceiveDocTypeForFrame:frame];
5394}
5395
36bb2ca2 5396- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
680eb135 5397 if ([frame parentFrame] == nil) {
7d2ac47f 5398 [self _finishLoading];
680eb135 5399
680eb135
JF
5400 if (DOMDocument *document = [frame DOMDocument])
5401 if (DOMNodeList<NSFastEnumeration> *bodies = [document getElementsByTagName:@"body"])
5402 for (DOMHTMLBodyElement *body in bodies) {
5403 DOMCSSStyleDeclaration *style([document getComputedStyle:body pseudoElement:nil]);
5404
5405 bool colored(false);
5406
5407 if (DOMCSSPrimitiveValue *color = static_cast<DOMCSSPrimitiveValue *>([style getPropertyCSSValue:@"background-color"])) {
5408 DOMRGBColor *rgb([color getRGBColorValue]);
5409
5410 float alpha([[rgb alpha] getFloatValue:DOM_CSS_NUMBER]);
68f1828e
JF
5411 NSLog(@"alpha:%g", alpha);
5412
680eb135
JF
5413 if (alpha != 0) {
5414 colored = true;
5415
9fe5e5f8 5416 [scroller_ setBackgroundColor:[UIColor
680eb135
JF
5417 colorWithRed:([[rgb red] getFloatValue:DOM_CSS_NUMBER] / 255)
5418 green:([[rgb green] getFloatValue:DOM_CSS_NUMBER] / 255)
5419 blue:([[rgb blue] getFloatValue:DOM_CSS_NUMBER] / 255)
5420 alpha:alpha
5421 ]];
5422 }
5423 }
5424
9fe5e5f8
JF
5425 if (!colored)
5426 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
680eb135
JF
5427 break;
5428 }
5429 }
5430
7d2ac47f 5431 return [webview_ webView:sender didFinishLoadForFrame:frame];
36bb2ca2 5432}
b4d89997 5433
36bb2ca2
JF
5434- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
5435 if ([frame parentFrame] != nil)
5436 return;
36bb2ca2 5437 [self _finishLoading];
7b0ce2da 5438
6d9712c4 5439 [self loadURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",
7b0ce2da 5440 [[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"error" ofType:@"html"]] absoluteString],
6d9712c4
JF
5441 [[error localizedDescription] stringByAddingPercentEscapes]
5442 ]]];
36bb2ca2
JF
5443}
5444
c390d3ab
JF
5445- (void) webView:(WebView *)sender addMessageToConsole:(NSDictionary *)dictionary {
5446#if ForSaurik
5447 lprintf("Console:%s\n", [[dictionary description] UTF8String]);
5448#endif
5449}
5450
770f2a8e 5451- (id) initWithBook:(RVBook *)book {
36bb2ca2 5452 if ((self = [super initWithBook:book]) != nil) {
36bb2ca2
JF
5453 loading_ = false;
5454
5455 struct CGRect bounds = [self bounds];
5456
36bb2ca2
JF
5457 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
5458 [self addSubview:scroller_];
5459
9fe5e5f8
JF
5460 [scroller_ setShowBackgroundShadow:NO];
5461 [scroller_ setFixedBackgroundPattern:YES];
5462 [scroller_ setBackgroundColor:[UIColor pinStripeColor]];
5463
36bb2ca2
JF
5464 [scroller_ setScrollingEnabled:YES];
5465 [scroller_ setAdjustForContentSizeChange:YES];
5466 [scroller_ setClipsSubviews:YES];
5467 [scroller_ setAllowsRubberBanding:YES];
5468 [scroller_ setScrollDecelerationFactor:0.99];
5469 [scroller_ setDelegate:self];
5470
5471 CGRect webrect = [scroller_ bounds];
5472 webrect.size.height = 0;
5473
affeffc7
JF
5474 WebView *webview;
5475
5476#if RecycleWebViews
baf80942
JF
5477 webview_ = [Documents_ lastObject];
5478 if (webview_ != nil) {
5479 webview_ = [webview_ retain];
affeffc7 5480 webview = [webview_ webView];
baf80942
JF
5481 [Documents_ removeLastObject];
5482 [webview_ setFrame:webrect];
5483 } else {
affeffc7
JF
5484#else
5485 if (true) {
5486#endif
baf80942 5487 webview_ = [[UIWebDocumentView alloc] initWithFrame:webrect];
affeffc7 5488 webview = [webview_ webView];
68f1828e
JF
5489
5490 // XXX: this is terribly (too?) expensive
fd7853a6
JF
5491 //[webview_ setDrawsBackground:NO];
5492 [webview setPreferencesIdentifier:@"Cydia"];
baf80942
JF
5493
5494 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
36bb2ca2 5495
affeffc7
JF
5496 [webview_ setAllowsMessaging:YES];
5497
baf80942 5498 [webview_ setTilingEnabled:YES];
affeffc7
JF
5499 [webview_ setDrawsGrid:NO];
5500 [webview_ setLogsTilingChanges:NO];
baf80942 5501 [webview_ setTileMinificationFilter:kCAFilterNearest];
affeffc7 5502 [webview_ setDetectsPhoneNumbers:NO];
baf80942 5503 [webview_ setAutoresizes:YES];
7d2ac47f 5504
68f1828e
JF
5505 [webview_ setMinimumScale:0.25f forDocumentTypes:0x10];
5506 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x10];
5507 [webview_ setViewportSize:CGSizeMake(980, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x10];
5508
5509 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x2];
5510
5511 [webview_ setMinimumScale:1.0f forDocumentTypes:0x8];
5512 [webview_ setInitialScale:UIWebViewScalesToFitScale forDocumentTypes:0x8];
5513 [webview_ setViewportSize:CGSizeMake(320, UIWebViewGrowsAndShrinksToFitHeight) forDocumentTypes:0x8];
7d2ac47f 5514
baf80942 5515 [webview_ _setDocumentType:0x4];
7d2ac47f 5516
baf80942
JF
5517 [webview_ setZoomsFocusedFormControl:YES];
5518 [webview_ setContentsPosition:7];
5519 [webview_ setEnabledGestures:0xa];
bf8476c8
JF
5520 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeIsZoomRubberBandEnabled];
5521 [webview_ setValue:[NSNumber numberWithBool:YES] forGestureAttribute:UIGestureAttributeUpdatesScroller];
affeffc7 5522
baf80942 5523 [webview_ setSmoothsFonts:YES];
c390d3ab 5524
affeffc7
JF
5525 [webview _setUsesLoaderCache:YES];
5526 [webview setGroupName:@"Cydia"];
68f1828e 5527 [webview _setLayoutInterval:0];
baf80942 5528 }
7d2ac47f 5529
36bb2ca2 5530 [webview_ setDelegate:self];
7d2ac47f 5531 [webview_ setGestureDelegate:self];
baf80942 5532 [scroller_ addSubview:webview_];
b4d89997 5533
c390d3ab
JF
5534 //NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5535
affeffc7 5536 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:UIProgressIndicatorStyleMediumWhite];
baf80942 5537 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 12, indsize.width, indsize.height)];
affeffc7 5538 [indicator_ setStyle:UIProgressIndicatorStyleMediumWhite];
36bb2ca2 5539
770f2a8e 5540 Package *package([[Database sharedInstance] packageWithName:@"cydia"]);
36bb2ca2
JF
5541 NSString *application = package == nil ? @"Cydia" : [NSString
5542 stringWithFormat:@"Cydia/%@",
5543 [package installed]
affeffc7 5544 ]; [webview setApplicationNameForUserAgent:application];
36bb2ca2 5545
baf80942
JF
5546 indirect_ = [[IndirectDelegate alloc] initWithDelegate:self];
5547
36bb2ca2 5548 [webview setFrameLoadDelegate:self];
baf80942 5549 [webview setResourceLoadDelegate:indirect_];
36bb2ca2 5550 [webview setUIDelegate:self];
c390d3ab
JF
5551 [webview setScriptDebugDelegate:self];
5552 [webview setPolicyDelegate:self];
36bb2ca2 5553
baf80942
JF
5554 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
5555 [scroller_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
36bb2ca2 5556 } return self;
dc5812ec
JF
5557}
5558
7d2ac47f
JF
5559- (void) didFinishGesturesInView:(UIView *)view forEvent:(id)event {
5560 [webview_ redrawScaledDocument];
5561}
5562
36bb2ca2 5563- (void) _rightButtonClicked {
9fe5e5f8
JF
5564 if (function_ == nil) {
5565 reloading_ = true;
5566 [self reloadURL];
5567 } else {
fd7853a6
JF
5568 WebView *webview([webview_ webView]);
5569 WebFrame *frame([webview mainFrame]);
5570
5571 id _private(MSHookIvar<id>(webview, "_private"));
5572 WebCore::Page *page(_private == nil ? NULL : MSHookIvar<WebCore::Page *>(_private, "page"));
5573 WebCore::Settings *settings(page == NULL ? NULL : page->settings());
5574
5575 bool no;
5576 if (settings == NULL)
5577 no = 0;
5578 else {
5579 no = settings->JavaScriptCanOpenWindowsAutomatically();
5580 settings->setJavaScriptCanOpenWindowsAutomatically(true);
5581 }
5582
6565453a 5583 [delegate_ clearFirstResponder];
9fe5e5f8 5584 JSObjectRef function([function_ JSObject]);
fd7853a6 5585 JSGlobalContextRef context([frame globalContext]);
9fe5e5f8 5586 JSObjectCallAsFunction(context, function, NULL, 0, NULL, NULL);
fd7853a6
JF
5587
5588 if (settings != NULL)
5589 settings->setJavaScriptCanOpenWindowsAutomatically(no);
9fe5e5f8 5590 }
36bb2ca2
JF
5591}
5592
fd7853a6 5593- (id) _rightButtonTitle {
9fe5e5f8 5594 return button_ != nil ? button_ : @"Reload";
6f1a15d9
JF
5595}
5596
fd7853a6 5597- (id) rightButtonTitle {
9fe5e5f8
JF
5598 return [self _loading] ? @"" : [self _rightButtonTitle];
5599}
5600
5601- (UINavigationButtonStyle) rightButtonStyle {
5602 if (style_ == nil) normal:
5603 return UINavigationButtonStyleNormal;
5604 else if ([style_ isEqualToString:@"Normal"])
5605 return UINavigationButtonStyleNormal;
5606 else if ([style_ isEqualToString:@"Back"])
5607 return UINavigationButtonStyleBack;
5608 else if ([style_ isEqualToString:@"Highlighted"])
5609 return UINavigationButtonStyleHighlighted;
5610 else if ([style_ isEqualToString:@"Destructive"])
5611 return UINavigationButtonStyleDestructive;
5612 else goto normal;
36bb2ca2
JF
5613}
5614
5615- (NSString *) title {
affeffc7 5616 return title_ == nil ? @"Loading" : title_;
36bb2ca2
JF
5617}
5618
5619- (NSString *) backButtonTitle {
5620 return @"Browser";
dc5812ec
JF
5621}
5622
36bb2ca2 5623- (void) setPageActive:(BOOL)active {
baf80942 5624 if (!active)
36bb2ca2 5625 [indicator_ removeFromSuperview];
baf80942
JF
5626 else
5627 [[book_ navigationBar] addSubview:indicator_];
dc5812ec
JF
5628}
5629
36bb2ca2 5630- (void) resetViewAnimated:(BOOL)animated {
b4d89997 5631}
2367a917 5632
a0376fc1
JF
5633- (void) setPushed:(bool)pushed {
5634 pushed_ = pushed;
5635}
5636
7b0ce2da
JF
5637@end
5638/* }}} */
aa5e5990 5639
807ae6d7 5640/* Cydia Book {{{ */
7b0ce2da
JF
5641@interface CYBook : RVBook <
5642 ProgressDelegate
5643> {
5644 _transient Database *database_;
baf80942 5645 UINavigationBar *overlay_;
affeffc7 5646 UINavigationBar *underlay_;
7b0ce2da
JF
5647 UIProgressIndicator *indicator_;
5648 UITextLabel *prompt_;
5649 UIProgressBar *progress_;
baf80942 5650 UINavigationButton *cancel_;
7b0ce2da 5651 bool updating_;
baf80942
JF
5652 size_t received_;
5653 NSTimeInterval last_;
aa5e5990
JF
5654}
5655
7b0ce2da
JF
5656- (id) initWithFrame:(CGRect)frame database:(Database *)database;
5657- (void) update;
5658- (BOOL) updating;
aa5e5990
JF
5659
5660@end
aa5e5990 5661
807ae6d7 5662@implementation CYBook
dc5812ec 5663
2367a917 5664- (void) dealloc {
807ae6d7
JF
5665 [overlay_ release];
5666 [indicator_ release];
5667 [prompt_ release];
5668 [progress_ release];
5669 [cancel_ release];
2367a917
JF
5670 [super dealloc];
5671}
5672
807ae6d7
JF
5673- (NSString *) getTitleForPage:(RVPage *)page {
5674 return Simplify([super getTitleForPage:page]);
b4d89997 5675}
dc5812ec 5676
807ae6d7
JF
5677- (BOOL) updating {
5678 return updating_;
6d9712c4
JF
5679}
5680
807ae6d7
JF
5681- (void) update {
5682 [UIView beginAnimations:nil context:NULL];
dc5812ec 5683
807ae6d7
JF
5684 CGRect ovrframe = [overlay_ frame];
5685 ovrframe.origin.y = 0;
5686 [overlay_ setFrame:ovrframe];
dc5812ec 5687
807ae6d7
JF
5688 CGRect barframe = [navbar_ frame];
5689 barframe.origin.y += ovrframe.size.height;
5690 [navbar_ setFrame:barframe];
dc5812ec 5691
807ae6d7
JF
5692 CGRect trnframe = [transition_ frame];
5693 trnframe.origin.y += ovrframe.size.height;
5694 trnframe.size.height -= ovrframe.size.height;
5695 [transition_ setFrame:trnframe];
5a09ae08 5696
807ae6d7 5697 [UIView endAnimations];
2367a917 5698
807ae6d7
JF
5699 [indicator_ startAnimation];
5700 [prompt_ setText:@"Updating Database"];
5701 [progress_ setProgress:0];
2367a917 5702
807ae6d7
JF
5703 received_ = 0;
5704 last_ = [NSDate timeIntervalSinceReferenceDate];
5705 updating_ = true;
5706 [overlay_ addSubview:cancel_];
2367a917 5707
807ae6d7
JF
5708 [NSThread
5709 detachNewThreadSelector:@selector(_update)
5710 toTarget:self
5711 withObject:nil
5712 ];
b4d89997 5713}
2367a917 5714
807ae6d7
JF
5715- (void) _update_ {
5716 updating_ = false;
36bb2ca2 5717
807ae6d7 5718 [indicator_ stopAnimation];
2367a917 5719
807ae6d7 5720 [UIView beginAnimations:nil context:NULL];
6d9712c4 5721
807ae6d7
JF
5722 CGRect ovrframe = [overlay_ frame];
5723 ovrframe.origin.y = -ovrframe.size.height;
5724 [overlay_ setFrame:ovrframe];
dc5812ec 5725
807ae6d7
JF
5726 CGRect barframe = [navbar_ frame];
5727 barframe.origin.y -= ovrframe.size.height;
5728 [navbar_ setFrame:barframe];
dc5812ec 5729
807ae6d7
JF
5730 CGRect trnframe = [transition_ frame];
5731 trnframe.origin.y -= ovrframe.size.height;
5732 trnframe.size.height += ovrframe.size.height;
5733 [transition_ setFrame:trnframe];
2367a917 5734
807ae6d7 5735 [UIView commitAnimations];
baf80942 5736
807ae6d7 5737 [delegate_ performSelector:@selector(reloadData) withObject:nil afterDelay:0];
2367a917
JF
5738}
5739
807ae6d7
JF
5740- (id) initWithFrame:(CGRect)frame database:(Database *)database {
5741 if ((self = [super initWithFrame:frame]) != nil) {
5742 database_ = database;
36bb2ca2 5743
807ae6d7
JF
5744 CGRect ovrrect = [navbar_ bounds];
5745 ovrrect.size.height = [UINavigationBar defaultSize].height;
5746 ovrrect.origin.y = -ovrrect.size.height;
6d9712c4 5747
807ae6d7
JF
5748 overlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5749 [self addSubview:overlay_];
dc5812ec 5750
807ae6d7
JF
5751 ovrrect.origin.y = frame.size.height;
5752 underlay_ = [[UINavigationBar alloc] initWithFrame:ovrrect];
5753 [underlay_ setTintColor:[UIColor colorWithRed:0.23 green:0.23 blue:0.23 alpha:1]];
5754 [self addSubview:underlay_];
6d9712c4 5755
807ae6d7
JF
5756 [overlay_ setBarStyle:1];
5757 [underlay_ setBarStyle:1];
5758
5759 int barstyle = [overlay_ _barStyle:NO];
5760 bool ugly = barstyle == 0;
5761
5762 UIProgressIndicatorStyle style = ugly ?
5763 UIProgressIndicatorStyleMediumBrown :
5764 UIProgressIndicatorStyleMediumWhite;
5765
5766 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
5767 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
5768 CGRect indrect = {{indoffset, indoffset}, indsize};
5769
5770 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
5771 [indicator_ setStyle:style];
5772 [overlay_ addSubview:indicator_];
5773
5774 CGSize prmsize = {215, indsize.height + 4};
5775
5776 CGRect prmrect = {{
5777 indoffset * 2 + indsize.width,
5778#ifdef __OBJC2__
5779 -1 +
5780#endif
5781 unsigned(ovrrect.size.height - prmsize.height) / 2
5782 }, prmsize};
5783
5784 UIFont *font = [UIFont systemFontOfSize:15];
5785
5786 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
5787
5788 [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
5789 [prompt_ setBackgroundColor:[UIColor clearColor]];
5790 [prompt_ setFont:font];
5791
5792 [overlay_ addSubview:prompt_];
5793
5794 CGSize prgsize = {75, 100};
5795
5796 CGRect prgrect = {{
5797 ovrrect.size.width - prgsize.width - 10,
5798 (ovrrect.size.height - prgsize.height) / 2
5799 } , prgsize};
5800
5801 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
5802 [progress_ setStyle:0];
5803 [overlay_ addSubview:progress_];
5804
5805 cancel_ = [[UINavigationButton alloc] initWithTitle:@"Cancel" style:UINavigationButtonStyleHighlighted];
5806 [cancel_ addTarget:self action:@selector(_onCancel) forControlEvents:UIControlEventTouchUpInside];
5807
5808 CGRect frame = [cancel_ frame];
5809 frame.size.width = 65;
5810 frame.origin.x = ovrrect.size.width - frame.size.width - 5;
5811 frame.origin.y = (ovrrect.size.height - frame.size.height) / 2;
5812 [cancel_ setFrame:frame];
5813
5814 [cancel_ setBarStyle:barstyle];
5815 } return self;
5816}
5817
5818- (void) _onCancel {
5819 updating_ = false;
5820 [cancel_ removeFromSuperview];
5821}
5822
5823- (void) _update { _pooled
5824 Status status;
5825 status.setDelegate(self);
5826
5827 [database_ updateWithStatus:status];
5828
5829 [self
5830 performSelectorOnMainThread:@selector(_update_)
5831 withObject:nil
5832 waitUntilDone:NO
5833 ];
5834}
5835
5836- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
5837 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
5838}
5839
5840- (void) setProgressTitle:(NSString *)title {
5841 [self
5842 performSelectorOnMainThread:@selector(_setProgressTitle:)
5843 withObject:title
5844 waitUntilDone:YES
5845 ];
5846}
5847
5848- (void) setProgressPercent:(float)percent {
5849 [self
5850 performSelectorOnMainThread:@selector(_setProgressPercent:)
5851 withObject:[NSNumber numberWithFloat:percent]
5852 waitUntilDone:YES
5853 ];
5854}
5855
5856- (void) startProgress {
5857}
5858
5859- (void) addProgressOutput:(NSString *)output {
5860 [self
5861 performSelectorOnMainThread:@selector(_addProgressOutput:)
5862 withObject:output
5863 waitUntilDone:YES
5864 ];
5865}
5866
5867- (bool) isCancelling:(size_t)received {
5868 NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
5869 if (received_ != received) {
5870 received_ = received;
5871 last_ = now;
5872 } else if (now - last_ > 15)
5873 return true;
5874 return !updating_;
5875}
5876
5877- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
5878 [sheet dismiss];
5879}
5880
5881- (void) _setProgressTitle:(NSString *)title {
5882 [prompt_ setText:title];
5883}
5884
5885- (void) _setProgressPercent:(NSNumber *)percent {
5886 [progress_ setProgress:[percent floatValue]];
5887}
5888
5889- (void) _addProgressOutput:(NSString *)output {
5890}
5891
5892@end
5893/* }}} */
5894/* Cydia:// Protocol {{{ */
5895@interface CydiaURLProtocol : NSURLProtocol {
5896}
5897
5898@end
5899
5900@implementation CydiaURLProtocol
5901
5902+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
5903 NSURL *url([request URL]);
5904 if (url == nil)
5905 return NO;
5906 NSString *scheme([[url scheme] lowercaseString]);
5907 if (scheme == nil || ![scheme isEqualToString:@"cydia"])
5908 return NO;
5909 return YES;
5910}
5911
5912+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
5913 return request;
5914}
5915
9fe5e5f8
JF
5916- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
5917 id<NSURLProtocolClient> client([self client]);
5918 NSData *data(UIImagePNGRepresentation(icon));
5919
5920 NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
5921 [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
5922 [client URLProtocol:self didLoadData:data];
5923 [client URLProtocolDidFinishLoading:self];
5924}
5925
807ae6d7
JF
5926- (void) startLoading {
5927 id<NSURLProtocolClient> client([self client]);
5928 NSURLRequest *request([self request]);
5929
5930 NSURL *url([request URL]);
5931 NSString *href([url absoluteString]);
5932
5933 NSString *path([href substringFromIndex:8]);
5934 NSRange slash([path rangeOfString:@"/"]);
5935
5936 NSString *command;
5937 if (slash.location == NSNotFound) {
5938 command = path;
5939 path = nil;
5940 } else {
5941 command = [path substringToIndex:slash.location];
5942 path = [path substringFromIndex:(slash.location + 1)];
5943 }
5944
5945 Database *database([Database sharedInstance]);
5946
5947 if ([command isEqualToString:@"package-icon"]) {
5948 if (path == nil)
5949 goto fail;
dec6029f 5950 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
807ae6d7
JF
5951 Package *package([database packageWithName:path]);
5952 if (package == nil)
5953 goto fail;
dec6029f 5954 UIImage *icon([package icon]);
9fe5e5f8 5955 [self _returnPNGWithImage:icon forRequest:request];
16f2786b
JF
5956 } else if ([command isEqualToString:@"source-icon"]) {
5957 if (path == nil)
5958 goto fail;
5959 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5960 NSString *source(Simplify(path));
16f2786b
JF
5961 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sources/%@.png", App_, source]]);
5962 if (icon == nil)
5963 icon = [UIImage applicationImageNamed:@"unknown.png"];
9fe5e5f8
JF
5964 [self _returnPNGWithImage:icon forRequest:request];
5965 } else if ([command isEqualToString:@"uikit-image"]) {
5966 if (path == nil)
5967 goto fail;
5968 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5969 UIImage *icon(_UIImageWithName(path));
5970 [self _returnPNGWithImage:icon forRequest:request];
dec6029f
JF
5971 } else if ([command isEqualToString:@"section-icon"]) {
5972 if (path == nil)
5973 goto fail;
5974 path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
5975 NSString *section(Simplify(path));
dec6029f
JF
5976 UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, section]]);
5977 if (icon == nil)
5978 icon = [UIImage applicationImageNamed:@"unknown.png"];
9fe5e5f8 5979 [self _returnPNGWithImage:icon forRequest:request];
807ae6d7
JF
5980 } else fail: {
5981 [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
5982 }
5983}
5984
5985- (void) stopLoading {
5986}
5987
5988@end
5989/* }}} */
5990
5991/* Install View {{{ */
5992@interface InstallView : RVPage {
5993 _transient Database *database_;
5994 NSMutableArray *sections_;
5995 NSMutableArray *filtered_;
5996 UITransitionView *transition_;
5997 UITable *list_;
5998 UIView *accessory_;
5999 BOOL editing_;
6000}
6001
6002- (id) initWithBook:(RVBook *)book database:(Database *)database;
6003- (void) reloadData;
6004- (void) resetView;
6005
6006@end
6007
6008@implementation InstallView
6009
6010- (void) dealloc {
6011 [list_ setDataSource:nil];
6012 [list_ setDelegate:nil];
6013
6014 [sections_ release];
6015 [filtered_ release];
6016 [transition_ release];
6017 [list_ release];
6018 [accessory_ release];
6019 [super dealloc];
6020}
6021
6022- (int) numberOfRowsInTable:(UITable *)table {
6023 return editing_ ? [sections_ count] : [filtered_ count] + 1;
6024}
6025
6026- (float) table:(UITable *)table heightForRow:(int)row {
6027 return 45;
6028}
6029
6030- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6031 if (reusing == nil)
6032 reusing = [[[SectionCell alloc] init] autorelease];
6033 [(SectionCell *)reusing setSection:(editing_ ?
6034 [sections_ objectAtIndex:row] :
6035 (row == 0 ? nil : [filtered_ objectAtIndex:(row - 1)])
6036 ) editing:editing_];
6037 return reusing;
6038}
6039
6040- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
6041 return !editing_;
6042}
6043
6044- (BOOL) table:(UITable *)table canSelectRow:(int)row {
6045 return !editing_;
6046}
6047
6048- (void) tableRowSelected:(NSNotification *)notification {
6049 int row = [[notification object] selectedRow];
6050 if (row == INT_MAX)
6051 return;
6052
6053 Section *section;
6054 NSString *name;
6055 NSString *title;
6056
6057 if (row == 0) {
6058 section = nil;
6059 name = nil;
6060 title = @"All Packages";
6061 } else {
6062 section = [filtered_ objectAtIndex:(row - 1)];
6063 name = [section name];
6064
6065 if (name != nil)
6066 title = name;
6067 else {
6068 name = @"";
6069 title = @"(No Section)";
6070 }
6071 }
6072
6073 PackageTable *table = [[[PackageTable alloc]
6074 initWithBook:book_
6075 database:database_
6076 title:title
6077 filter:@selector(isVisiblyUninstalledInSection:)
6078 with:name
6079 ] autorelease];
6080
6081 [table setDelegate:delegate_];
6082
6083 [book_ pushPage:table];
6084}
6085
6086- (id) initWithBook:(RVBook *)book database:(Database *)database {
6087 if ((self = [super initWithBook:book]) != nil) {
6088 database_ = database;
6089
6090 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
6091 filtered_ = [[NSMutableArray arrayWithCapacity:16] retain];
6092
6093 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
6094 [self addSubview:transition_];
6095
6096 list_ = [[UITable alloc] initWithFrame:[transition_ bounds]];
6097 [transition_ transition:0 toView:list_];
6098
6099 UITableColumn *column = [[[UITableColumn alloc]
6100 initWithTitle:@"Name"
6101 identifier:@"name"
6102 width:[self frame].size.width
6103 ] autorelease];
6104
6105 [list_ setDataSource:self];
6106 [list_ setSeparatorStyle:1];
6107 [list_ addTableColumn:column];
6108 [list_ setDelegate:self];
6109 [list_ setReusesTableCells:YES];
6110
6111 [self reloadData];
6112
6113 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6114 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6115 } return self;
6116}
6117
6118- (void) reloadData {
6119 NSArray *packages = [database_ packages];
6120
6121 [sections_ removeAllObjects];
6122 [filtered_ removeAllObjects];
6123
6124 NSMutableArray *filtered = [NSMutableArray arrayWithCapacity:[packages count]];
6125 NSMutableDictionary *sections = [NSMutableDictionary dictionaryWithCapacity:32];
6126
6127 _trace();
6128 for (size_t i(0); i != [packages count]; ++i) {
6129 Package *package([packages objectAtIndex:i]);
6130 NSString *name([package section]);
6131
6132 if (name != nil) {
6133 Section *section([sections objectForKey:name]);
6134 if (section == nil) {
6135 section = [[[Section alloc] initWithName:name] autorelease];
6136 [sections setObject:section forKey:name];
6137 }
6138 }
6d9712c4
JF
6139
6140 if ([package valid] && [package installed] == nil && [package visible])
6141 [filtered addObject:package];
b4d89997 6142 }
807ae6d7 6143 _trace();
dc5812ec 6144
6d9712c4
JF
6145 [sections_ addObjectsFromArray:[sections allValues]];
6146 [sections_ sortUsingSelector:@selector(compareByName:)];
6147
807ae6d7 6148 _trace();
6d9712c4 6149 [filtered sortUsingSelector:@selector(compareBySection:)];
807ae6d7 6150 _trace();
dc5812ec 6151
b4d89997 6152 Section *section = nil;
6d9712c4
JF
6153 for (size_t offset = 0, count = [filtered count]; offset != count; ++offset) {
6154 Package *package = [filtered objectAtIndex:offset];
b4d89997 6155 NSString *name = [package section];
2367a917 6156
36bb2ca2 6157 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
6d9712c4
JF
6158 section = name == nil ?
6159 [[[Section alloc] initWithName:nil] autorelease] :
6160 [sections objectForKey:name];
6161 [filtered_ addObject:section];
b4d89997 6162 }
dc5812ec 6163
36bb2ca2 6164 [section addToCount];
b4d89997 6165 }
807ae6d7 6166 _trace();
2367a917 6167
b4d89997 6168 [list_ reloadData];
807ae6d7 6169 _trace();
36bb2ca2 6170}
2367a917 6171
6d9712c4
JF
6172- (void) resetView {
6173 if (editing_)
6174 [self _rightButtonClicked];
6175}
6176
36bb2ca2
JF
6177- (void) resetViewAnimated:(BOOL)animated {
6178 [list_ resetViewAnimated:animated];
6179}
2367a917 6180
6d9712c4
JF
6181- (void) _rightButtonClicked {
6182 if ((editing_ = !editing_))
6183 [list_ reloadData];
807ae6d7 6184 else
6d9712c4 6185 [delegate_ updateData];
affeffc7 6186 [book_ reloadTitleForPage:self];
6d9712c4
JF
6187 [book_ reloadButtonsForPage:self];
6188}
6189
36bb2ca2 6190- (NSString *) title {
6d9712c4 6191 return editing_ ? @"Section Visibility" : @"Install by Section";
2367a917
JF
6192}
6193
36bb2ca2
JF
6194- (NSString *) backButtonTitle {
6195 return @"Sections";
dc5812ec
JF
6196}
6197
fd7853a6 6198- (id) rightButtonTitle {
6d9712c4
JF
6199 return [sections_ count] == 0 ? nil : editing_ ? @"Done" : @"Edit";
6200}
6201
baf80942
JF
6202- (UINavigationButtonStyle) rightButtonStyle {
6203 return editing_ ? UINavigationButtonStyleHighlighted : UINavigationButtonStyleNormal;
6204}
6205
a54b1c10
JF
6206- (UIView *) accessoryView {
6207 return accessory_;
6208}
6209
dc5812ec 6210@end
2367a917 6211/* }}} */
b4d89997 6212/* Changes View {{{ */
36bb2ca2
JF
6213@interface ChangesView : RVPage {
6214 _transient Database *database_;
dc5812ec
JF
6215 NSMutableArray *packages_;
6216 NSMutableArray *sections_;
36bb2ca2
JF
6217 UISectionList *list_;
6218 unsigned upgrades_;
dc5812ec
JF
6219}
6220
36bb2ca2
JF
6221- (id) initWithBook:(RVBook *)book database:(Database *)database;
6222- (void) reloadData;
dc5812ec 6223
dc5812ec
JF
6224@end
6225
b4d89997
JF
6226@implementation ChangesView
6227
6228- (void) dealloc {
36bb2ca2
JF
6229 [[list_ table] setDelegate:nil];
6230 [list_ setDataSource:nil];
6231
b4d89997
JF
6232 [packages_ release];
6233 [sections_ release];
36bb2ca2 6234 [list_ release];
b4d89997
JF
6235 [super dealloc];
6236}
dc5812ec
JF
6237
6238- (int) numberOfSectionsInSectionList:(UISectionList *)list {
6239 return [sections_ count];
6240}
6241
6242- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
6243 return [[sections_ objectAtIndex:section] name];
6244}
6245
6246- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
6247 return [[sections_ objectAtIndex:section] row];
6248}
6249
6250- (int) numberOfRowsInTable:(UITable *)table {
6251 return [packages_ count];
6252}
6253
6254- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 6255 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
dc5812ec
JF
6256}
6257
1d80f6b9
JF
6258- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
6259 if (reusing == nil)
36bb2ca2 6260 reusing = [[[PackageCell alloc] init] autorelease];
1d80f6b9
JF
6261 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
6262 return reusing;
dc5812ec
JF
6263}
6264
6265- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
b4d89997 6266 return NO;
dc5812ec
JF
6267}
6268
b4d89997
JF
6269- (void) tableRowSelected:(NSNotification *)notification {
6270 int row = [[notification object] selectedRow];
36bb2ca2
JF
6271 if (row == INT_MAX)
6272 return;
6273 Package *package = [packages_ objectAtIndex:row];
6274 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
6275 [view setDelegate:delegate_];
6276 [view setPackage:package];
6277 [book_ pushPage:view];
dc5812ec
JF
6278}
6279
9a7b04c5
JF
6280- (void) _leftButtonClicked {
6281 [(CYBook *)book_ update];
6282 [self reloadButtons];
6283}
6284
36bb2ca2
JF
6285- (void) _rightButtonClicked {
6286 [delegate_ distUpgrade];
dc5812ec
JF
6287}
6288
36bb2ca2
JF
6289- (id) initWithBook:(RVBook *)book database:(Database *)database {
6290 if ((self = [super initWithBook:book]) != nil) {
6291 database_ = database;
dc5812ec 6292
b4d89997
JF
6293 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
6294 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 6295
36bb2ca2
JF
6296 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
6297 [self addSubview:list_];
6298
3178d79b 6299 [list_ setShouldHideHeaderInShortLists:NO];
b4d89997
JF
6300 [list_ setDataSource:self];
6301 //[list_ setSectionListStyle:1];
dc5812ec 6302
b4d89997 6303 UITableColumn *column = [[[UITableColumn alloc]
dc5812ec
JF
6304 initWithTitle:@"Name"
6305 identifier:@"name"
36bb2ca2 6306 width:[self frame].size.width
b4d89997 6307 ] autorelease];
dc5812ec
JF
6308
6309 UITable *table = [list_ table];
6310 [table setSeparatorStyle:1];
6311 [table addTableColumn:column];
6312 [table setDelegate:self];
1d80f6b9 6313 [table setReusesTableCells:YES];
dc5812ec 6314
36bb2ca2 6315 [self reloadData];
baf80942
JF
6316
6317 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6318 [list_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
dc5812ec
JF
6319 } return self;
6320}
6321
36bb2ca2
JF
6322- (void) reloadData {
6323 NSArray *packages = [database_ packages];
6324
b4d89997 6325 [packages_ removeAllObjects];
36bb2ca2
JF
6326 [sections_ removeAllObjects];
6327
807ae6d7 6328 _trace();
b4d89997
JF
6329 for (size_t i(0); i != [packages count]; ++i) {
6330 Package *package([packages objectAtIndex:i]);
6d9712c4
JF
6331
6332 if (
6333 [package installed] == nil && [package valid] && [package visible] ||
6334 [package upgradableAndEssential:NO]
6335 )
b4d89997
JF
6336 [packages_ addObject:package];
6337 }
2367a917 6338
807ae6d7 6339 _trace();
9487f027 6340 [packages_ radixSortUsingSelector:@selector(compareForChanges) withObject:nil];
807ae6d7 6341 _trace();
dc5812ec 6342
6d9712c4 6343 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades"] autorelease];
807ae6d7 6344 Section *ignored = [[[Section alloc] initWithName:@"Ignored Upgrades"] autorelease];
dc5812ec 6345 Section *section = nil;
807ae6d7 6346 NSDate *last = nil;
b4d89997 6347
36bb2ca2
JF
6348 upgrades_ = 0;
6349 bool unseens = false;
6350
199d0ba5 6351 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
36bb2ca2 6352
807ae6d7 6353 _trace();
dc5812ec
JF
6354 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
6355 Package *package = [packages_ objectAtIndex:offset];
dc5812ec 6356
807ae6d7 6357 if (![package upgradableAndEssential:YES]) {
36bb2ca2 6358 unseens = true;
b4d89997 6359 NSDate *seen = [package seen];
dc5812ec 6360
807ae6d7
JF
6361 if (section == nil || last != seen && (seen == nil || [seen compare:last] != NSOrderedSame)) {
6362 last = seen;
723a0072 6363
807ae6d7 6364 NSString *name(seen == nil ? [@"n/a ?" retain] : (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen));
b4d89997
JF
6365 section = [[[Section alloc] initWithName:name row:offset] autorelease];
6366 [sections_ addObject:section];
807ae6d7 6367 [name release];
b4d89997
JF
6368 }
6369
36bb2ca2 6370 [section addToCount];
807ae6d7
JF
6371 } else if ([package ignored])
6372 [ignored addToCount];
6373 else {
6374 ++upgrades_;
6375 [upgradable addToCount];
b4d89997 6376 }
dc5812ec 6377 }
807ae6d7 6378 _trace();
dc5812ec 6379
36bb2ca2 6380 CFRelease(formatter);
b4d89997 6381
36bb2ca2
JF
6382 if (unseens) {
6383 Section *last = [sections_ lastObject];
6384 size_t count = [last count];
6385 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
6386 [sections_ removeLastObject];
6387 }
dc5812ec 6388
807ae6d7
JF
6389 if ([ignored count] != 0)
6390 [sections_ insertObject:ignored atIndex:0];
36bb2ca2
JF
6391 if (upgrades_ != 0)
6392 [sections_ insertObject:upgradable atIndex:0];
c25a610d 6393
36bb2ca2
JF
6394 [list_ reloadData];
6395 [self reloadButtons];
3178d79b
JF
6396}
6397
36bb2ca2
JF
6398- (void) resetViewAnimated:(BOOL)animated {
6399 [list_ resetViewAnimated:animated];
3178d79b
JF
6400}
6401
9a7b04c5
JF
6402- (NSString *) leftButtonTitle {
6403 return [(CYBook *)book_ updating] ? nil : @"Refresh";
6404}
6405
fd7853a6 6406- (id) rightButtonTitle {
f641a0e5 6407 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade (%u)", upgrades_];
3178d79b
JF
6408}
6409
36bb2ca2
JF
6410- (NSString *) title {
6411 return @"Changes";
8fe19fc1
JF
6412}
6413
3178d79b
JF
6414@end
6415/* }}} */
36bb2ca2
JF
6416/* Search View {{{ */
6417@protocol SearchViewDelegate
6418- (void) showKeyboard:(BOOL)show;
6419@end
b4d89997 6420
a0be02eb 6421@interface SearchView : RVPage {
36bb2ca2
JF
6422 UIView *accessory_;
6423 UISearchField *field_;
a0be02eb
JF
6424 UITransitionView *transition_;
6425 PackageTable *table_;
6426 UIPreferencesTable *advanced_;
9b619239 6427 UIView *dimmed_;
a0be02eb 6428 bool flipped_;
ab398adf 6429 bool reload_;
36bb2ca2 6430}
b4d89997 6431
36bb2ca2
JF
6432- (id) initWithBook:(RVBook *)book database:(Database *)database;
6433- (void) reloadData;
b4d89997 6434
3178d79b
JF
6435@end
6436
36bb2ca2 6437@implementation SearchView
3178d79b 6438
b4d89997 6439- (void) dealloc {
36bb2ca2 6440 [field_ setDelegate:nil];
3178d79b 6441
36bb2ca2
JF
6442 [accessory_ release];
6443 [field_ release];
a0be02eb
JF
6444 [transition_ release];
6445 [table_ release];
6446 [advanced_ release];
9b619239 6447 [dimmed_ release];
36bb2ca2 6448 [super dealloc];
b4d89997
JF
6449}
6450
ab398adf
JF
6451- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6452 return 1;
6453}
6454
6455- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6456 switch (group) {
6457 case 0: return @"Advanced Search (Coming Soon!)";
6458
6459 default: _assert(false);
6460 }
6461}
6462
6463- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6464 switch (group) {
6465 case 0: return 0;
6466
6467 default: _assert(false);
6468 }
6469}
6470
2388b078
JF
6471- (void) _showKeyboard:(BOOL)show {
6472 CGSize keysize = [UIKeyboard defaultSize];
6473 CGRect keydown = [book_ pageBounds];
6474 CGRect keyup = keydown;
6475 keyup.size.height -= keysize.height - ButtonBarHeight_;
6476
6477 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
6478
6479 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
6480 [animation setSignificantRectFields:8];
6481
6482 if (show) {
6483 [animation setStartFrame:keydown];
6484 [animation setEndFrame:keyup];
6485 } else {
6486 [animation setStartFrame:keyup];
6487 [animation setEndFrame:keydown];
6488 }
6489
6490 UIAnimator *animator = [UIAnimator sharedAnimator];
6491
6492 [animator
6493 addAnimations:[NSArray arrayWithObjects:animation, nil]
6494 withDuration:(KeyboardTime_ - delay)
6495 start:!show
6496 ];
6497
6498 if (show)
6499 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
6500
6501 [delegate_ showKeyboard:show];
6502}
6503
36bb2ca2 6504- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2388b078 6505 [self _showKeyboard:YES];
36bb2ca2 6506}
b4d89997 6507
36bb2ca2 6508- (void) textFieldDidResignFirstResponder:(UITextField *)field {
2388b078 6509 [self _showKeyboard:NO];
36bb2ca2 6510}
b4d89997 6511
36bb2ca2 6512- (void) keyboardInputChanged:(UIFieldEditor *)editor {
ab398adf
JF
6513 if (reload_) {
6514 NSString *text([field_ text]);
6515 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
6516 [self reloadData];
6517 reload_ = false;
6518 }
6519}
6520
6521- (void) textFieldClearButtonPressed:(UITextField *)field {
6522 reload_ = true;
6523}
6524
6525- (void) keyboardInputShouldDelete:(id)input {
6526 reload_ = true;
36bb2ca2 6527}
b4d89997 6528
36bb2ca2 6529- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
ab398adf
JF
6530 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
6531 reload_ = true;
36bb2ca2 6532 return YES;
ab398adf
JF
6533 } else {
6534 [field_ resignFirstResponder];
6535 return NO;
6536 }
b4d89997
JF
6537}
6538
36bb2ca2 6539- (id) initWithBook:(RVBook *)book database:(Database *)database {
a0be02eb
JF
6540 if ((self = [super initWithBook:book]) != nil) {
6541 CGRect pageBounds = [book_ pageBounds];
6542
7fea16f2 6543 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
a0be02eb 6544 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
7fea16f2 6545 [self addSubview:pinstripe];*/
a0be02eb 6546
a0be02eb
JF
6547 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
6548 [self addSubview:transition_];
6549
6550 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
6551
ab398adf
JF
6552 [advanced_ setReusesTableCells:YES];
6553 [advanced_ setDataSource:self];
6554 [advanced_ reloadData];
6555
9b619239
JF
6556 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
6557 CGColor dimmed(space_, 0, 0, 0, 0.5);
a9543575 6558 [dimmed_ setBackgroundColor:[UIColor colorWithCGColor:dimmed]];
9b619239 6559
a0be02eb
JF
6560 table_ = [[PackageTable alloc]
6561 initWithBook:book
6562 database:database
6563 title:nil
7d2ac47f 6564 filter:@selector(isUnfilteredAndSearchedForBy:)
a0be02eb
JF
6565 with:nil
6566 ];
6567
ab398adf 6568 [table_ setShouldHideHeaderInShortLists:NO];
a0be02eb
JF
6569 [transition_ transition:0 toView:table_];
6570
8cc774c5
JF
6571 CGRect cnfrect = {{
6572#ifdef __OBJC2__
6573 6 +
6574#endif
6575 1, 38}, {17, 18}};
b4d89997 6576
36bb2ca2 6577 CGRect area;
6d9712c4 6578 area.origin.x = /*cnfrect.origin.x + cnfrect.size.width + 4 +*/ 10;
baf80942 6579 area.origin.y = 1;
8cc774c5
JF
6580
6581 area.size.width =
6582#ifdef __OBJC2__
6583 8 +
6584#endif
6585 [self bounds].size.width - area.origin.x - 18;
6586
36bb2ca2 6587 area.size.height = [UISearchField defaultHeight];
3178d79b 6588
36bb2ca2 6589 field_ = [[UISearchField alloc] initWithFrame:area];
b4d89997 6590
a9543575 6591 UIFont *font = [UIFont systemFontOfSize:16];
36bb2ca2 6592 [field_ setFont:font];
b4d89997 6593
36bb2ca2 6594 [field_ setPlaceholder:@"Package Names & Descriptions"];
36bb2ca2 6595 [field_ setDelegate:self];
b4d89997 6596
87f46a96 6597 [field_ setPaddingTop:5];
8cc774c5 6598
b49f4c92
JF
6599 UITextInputTraits *traits([field_ textInputTraits]);
6600 [traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
6601 [traits setAutocorrectionType:UITextAutocorrectionTypeNo];
6602 [traits setReturnKeyType:UIReturnKeySearch];
6d9712c4 6603
baf80942 6604 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height}};
8cc774c5
JF
6605
6606 accessory_ = [[UIView alloc] initWithFrame:accrect];
795d26fc
JF
6607 [accessory_ addSubview:field_];
6608
6d9712c4 6609 /*UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
36bb2ca2 6610 [configure setShowPressFeedback:YES];
559dec34 6611 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
36bb2ca2 6612 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
6d9712c4 6613 [accessory_ addSubview:configure];*/
baf80942
JF
6614
6615 [self setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
6616 [table_ setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
36bb2ca2 6617 } return self;
3178d79b
JF
6618}
6619
a0be02eb 6620- (void) flipPage {
199d0ba5 6621#ifndef __OBJC2__
a0be02eb
JF
6622 LKAnimation *animation = [LKTransition animation];
6623 [animation setType:@"oglFlip"];
6624 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
6625 [animation setFillMode:@"extended"];
6626 [animation setTransitionFlags:3];
6627 [animation setDuration:10];
6628 [animation setSpeed:0.35];
7fea16f2
JF
6629 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
6630 [[transition_ _layer] addAnimation:animation forKey:0];
a0be02eb
JF
6631 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
6632 flipped_ = !flipped_;
199d0ba5 6633#endif
a0be02eb
JF
6634}
6635
36bb2ca2 6636- (void) configurePushed {
a0be02eb
JF
6637 [field_ resignFirstResponder];
6638 [self flipPage];
6639}
6640
807ae6d7
JF
6641- (void) resetViewAnimated:(BOOL)animated {
6642 if (flipped_)
6643 [self flipPage];
6644 [table_ resetViewAnimated:animated];
6645}
3178d79b 6646
807ae6d7
JF
6647- (void) _reloadData {
6648}
b4d89997 6649
807ae6d7
JF
6650- (void) reloadData {
6651 if (flipped_)
6652 [self flipPage];
6653 [table_ setObject:[field_ text]];
6654 [table_ reloadData];
6655 [table_ resetCursor];
6656}
affeffc7 6657
807ae6d7
JF
6658- (UIView *) accessoryView {
6659 return accessory_;
6660}
affeffc7 6661
807ae6d7
JF
6662- (NSString *) title {
6663 return nil;
6664}
dddbc481 6665
807ae6d7
JF
6666- (NSString *) backButtonTitle {
6667 return @"Search";
6668}
795d26fc 6669
807ae6d7
JF
6670- (void) setDelegate:(id)delegate {
6671 [table_ setDelegate:delegate];
6672 [super setDelegate:delegate];
6673}
8fe19fc1 6674
807ae6d7
JF
6675@end
6676/* }}} */
b4d89997 6677
807ae6d7
JF
6678@interface SettingsView : RVPage {
6679 _transient Database *database_;
6680 NSString *name_;
6681 Package *package_;
6682 UIPreferencesTable *table_;
6683 _UISwitchSlider *subscribedSwitch_;
6684 _UISwitchSlider *ignoredSwitch_;
6685 UIPreferencesControlTableCell *subscribedCell_;
6686 UIPreferencesControlTableCell *ignoredCell_;
6687}
b4d89997 6688
807ae6d7 6689- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
b4d89997 6690
807ae6d7 6691@end
b4d89997 6692
807ae6d7 6693@implementation SettingsView
b4d89997 6694
807ae6d7
JF
6695- (void) dealloc {
6696 [table_ setDataSource:nil];
b4d89997 6697
807ae6d7
JF
6698 [name_ release];
6699 if (package_ != nil)
6700 [package_ release];
6701 [table_ release];
6702 [subscribedSwitch_ release];
6703 [ignoredSwitch_ release];
6704 [subscribedCell_ release];
6705 [ignoredCell_ release];
6706 [super dealloc];
6707}
b4d89997 6708
807ae6d7
JF
6709- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
6710 if (package_ == nil)
6711 return 0;
b4d89997 6712
807ae6d7
JF
6713 return 2;
6714}
36bb2ca2 6715
807ae6d7
JF
6716- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
6717 if (package_ == nil)
6718 return nil;
baf80942 6719
807ae6d7
JF
6720 switch (group) {
6721 case 0: return nil;
6722 case 1: return nil;
baf80942 6723
807ae6d7
JF
6724 default: _assert(false);
6725 }
baf80942 6726
807ae6d7 6727 return nil;
3178d79b
JF
6728}
6729
807ae6d7
JF
6730- (BOOL) preferencesTable:(UIPreferencesTable *)table isLabelGroup:(int)group {
6731 if (package_ == nil)
6732 return NO;
baf80942 6733
807ae6d7
JF
6734 switch (group) {
6735 case 0: return NO;
6736 case 1: return YES;
b4d89997 6737
807ae6d7
JF
6738 default: _assert(false);
6739 }
3178d79b 6740
807ae6d7 6741 return NO;
dc5812ec
JF
6742}
6743
807ae6d7
JF
6744- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
6745 if (package_ == nil)
6746 return 0;
dc5812ec 6747
807ae6d7
JF
6748 switch (group) {
6749 case 0: return 1;
6750 case 1: return 1;
3178d79b 6751
807ae6d7
JF
6752 default: _assert(false);
6753 }
baf80942 6754
807ae6d7 6755 return 0;
36bb2ca2 6756}
b4d89997 6757
807ae6d7
JF
6758- (void) onSomething:(UIPreferencesControlTableCell *)cell withKey:(NSString *)key {
6759 if (package_ == nil)
6760 return;
b4d89997 6761
807ae6d7 6762 _UISwitchSlider *slider([cell control]);
807ae6d7
JF
6763 BOOL value([slider value] != 0);
6764 NSMutableDictionary *metadata([package_ metadata]);
baf80942 6765
807ae6d7
JF
6766 BOOL before;
6767 if (NSNumber *number = [metadata objectForKey:key])
6768 before = [number boolValue];
6769 else
6770 before = NO;
36bb2ca2 6771
807ae6d7
JF
6772 if (value != before) {
6773 [metadata setObject:[NSNumber numberWithBool:value] forKey:key];
6774 Changed_ = true;
807ae6d7
JF
6775 [delegate_ updateData];
6776 }
baf80942
JF
6777}
6778
807ae6d7
JF
6779- (void) onSubscribed:(UIPreferencesControlTableCell *)cell {
6780 [self onSomething:cell withKey:@"IsSubscribed"];
36bb2ca2 6781}
b4d89997 6782
807ae6d7
JF
6783- (void) onIgnored:(UIPreferencesControlTableCell *)cell {
6784 [self onSomething:cell withKey:@"IsIgnored"];
8fe19fc1
JF
6785}
6786
807ae6d7
JF
6787- (id) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
6788 if (package_ == nil)
6789 return nil;
770f2a8e 6790
807ae6d7
JF
6791 switch (group) {
6792 case 0: switch (row) {
6793 case 0:
6794 return subscribedCell_;
6795 case 1:
6796 return ignoredCell_;
6797 default: _assert(false);
6798 } break;
6799
6800 case 1: switch (row) {
6801 case 0: {
6802 UIPreferencesControlTableCell *cell([[[UIPreferencesControlTableCell alloc] init] autorelease]);
9487f027
JF
6803 [cell setShowSelection:NO];
6804 [cell setTitle:@"Changes only shows upgrades to installed packages so as to minimize spam from packagers. Activate this to see upgrades to this package even when it is not installed."];
807ae6d7
JF
6805 return cell;
6806 }
770f2a8e 6807
807ae6d7
JF
6808 default: _assert(false);
6809 } break;
770f2a8e 6810
807ae6d7
JF
6811 default: _assert(false);
6812 }
770f2a8e 6813
807ae6d7 6814 return nil;
770f2a8e
JF
6815}
6816
807ae6d7
JF
6817- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6818 if ((self = [super initWithBook:book])) {
6819 database_ = database;
6820 name_ = [package retain];
770f2a8e 6821
807ae6d7
JF
6822 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
6823 [self addSubview:table_];
770f2a8e 6824
807ae6d7
JF
6825 subscribedSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6826 [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:kUIControlEventMouseUpInside];
770f2a8e 6827
807ae6d7
JF
6828 ignoredSwitch_ = [[_UISwitchSlider alloc] initWithFrame:CGRectMake(200, 10, 50, 20)];
6829 [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:kUIControlEventMouseUpInside];
770f2a8e 6830
807ae6d7 6831 subscribedCell_ = [[UIPreferencesControlTableCell alloc] init];
9487f027 6832 [subscribedCell_ setShowSelection:NO];
807ae6d7
JF
6833 [subscribedCell_ setTitle:@"Show All Changes"];
6834 [subscribedCell_ setControl:subscribedSwitch_];
770f2a8e 6835
807ae6d7 6836 ignoredCell_ = [[UIPreferencesControlTableCell alloc] init];
9487f027 6837 [ignoredCell_ setShowSelection:NO];
807ae6d7
JF
6838 [ignoredCell_ setTitle:@"Ignore Upgrades"];
6839 [ignoredCell_ setControl:ignoredSwitch_];
770f2a8e 6840
807ae6d7
JF
6841 [table_ setDataSource:self];
6842 [self reloadData];
6843 } return self;
6844}
770f2a8e 6845
807ae6d7
JF
6846- (void) resetViewAnimated:(BOOL)animated {
6847 [table_ resetViewAnimated:animated];
6848}
770f2a8e 6849
807ae6d7
JF
6850- (void) reloadData {
6851 if (package_ != nil)
6852 [package_ autorelease];
6853 package_ = [database_ packageWithName:name_];
6854 if (package_ != nil) {
6855 [package_ retain];
6856 [subscribedSwitch_ setValue:([package_ subscribed] ? 1 : 0) animated:NO];
6857 [ignoredSwitch_ setValue:([package_ ignored] ? 1 : 0) animated:NO];
770f2a8e 6858 }
807ae6d7
JF
6859
6860 [table_ reloadData];
770f2a8e
JF
6861}
6862
807ae6d7
JF
6863- (NSString *) title {
6864 return @"Settings";
770f2a8e
JF
6865}
6866
6867@end
6868
807ae6d7 6869/* Signature View {{{ */
770f2a8e
JF
6870@interface SignatureView : BrowserView {
6871 _transient Database *database_;
6872 NSString *package_;
6873}
6874
6875- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package;
6876
6877@end
6878
6879@implementation SignatureView
6880
6881- (void) dealloc {
6882 [package_ release];
6883 [super dealloc];
6884}
6885
6886- (void) webView:(WebView *)sender didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
6887 // XXX: dude!
6888 [super webView:sender didClearWindowObject:window forFrame:frame];
6889}
6890
6891- (id) initWithBook:(RVBook *)book database:(Database *)database package:(NSString *)package {
6892 if ((self = [super initWithBook:book]) != nil) {
6893 database_ = database;
6894 package_ = [package retain];
6895 [self reloadData];
6896 } return self;
6897}
6898
807ae6d7
JF
6899- (void) resetViewAnimated:(BOOL)animated {
6900}
6901
770f2a8e
JF
6902- (void) reloadData {
6903 [self loadURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"signature" ofType:@"html"]]];
6904}
6905
6906@end
807ae6d7 6907/* }}} */
770f2a8e 6908
2367a917 6909@interface Cydia : UIApplication <
3178d79b 6910 ConfirmationViewDelegate,
b4d89997 6911 ProgressViewDelegate,
5f6bff8c
JF
6912 SearchViewDelegate,
6913 CydiaDelegate
2367a917 6914> {
dc5812ec 6915 UIWindow *window_;
36bb2ca2 6916
3178d79b
JF
6917 UIView *underlay_;
6918 UIView *overlay_;
36bb2ca2 6919 CYBook *book_;
a9543575 6920 UIToolbar *buttonbar_;
3178d79b 6921
affeffc7 6922 RVBook *confirm_;
9bedffaa 6923
a54b1c10 6924 NSMutableArray *essential_;
9bedffaa 6925 NSMutableArray *broken_;
dc5812ec
JF
6926
6927 Database *database_;
6928 ProgressView *progress_;
6929
06aa974d 6930 unsigned tag_;
b4d89997
JF
6931
6932 UIKeyboard *keyboard_;
bd150f54 6933 UIProgressHUD *hud_;
9b619239
JF
6934
6935 InstallView *install_;
6936 ChangesView *changes_;
6937 ManageView *manage_;
6938 SearchView *search_;
dc5812ec
JF
6939}
6940
dc5812ec
JF
6941@end
6942
6943@implementation Cydia
6944
9bedffaa
JF
6945- (void) _loaded {
6946 if ([broken_ count] != 0) {
6947 int count = [broken_ count];
6948
a9543575 6949 UIActionSheet *sheet = [[[UIActionSheet alloc]
9bedffaa
JF
6950 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
6951 buttons:[NSArray arrayWithObjects:
6952 @"Forcibly Clear",
6953 @"Ignore (Temporary)",
6954 nil]
6955 defaultButtonIndex:0
6956 delegate:self
6957 context:@"fixhalf"
6958 ] autorelease];
6959
31f3cfff 6960 [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
6961 [sheet popupAlertAnimated:YES];
6962 } else if (!Ignored_ && [essential_ count] != 0) {
6963 int count = [essential_ count];
6964
a9543575 6965 UIActionSheet *sheet = [[[UIActionSheet alloc]
9bedffaa 6966 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
affeffc7
JF
6967 buttons:[NSArray arrayWithObjects:
6968 @"Upgrade Essential",
9487f027 6969 @"Complete Upgrade",
affeffc7
JF
6970 @"Ignore (Temporary)",
6971 nil]
9bedffaa
JF
6972 defaultButtonIndex:0
6973 delegate:self
6974 context:@"upgrade"
6975 ] autorelease];
6976
31f3cfff 6977 [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
6978 [sheet popupAlertAnimated:YES];
6979 }
6980}
6981
36bb2ca2
JF
6982- (void) _reloadData {
6983 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
6984 [hud setText:@"Reloading Data"];
6985 [overlay_ addSubview:hud];
6986 [hud show:YES];*/
dc5812ec 6987
dc5812ec 6988 [database_ reloadData];
c25a610d 6989
36bb2ca2 6990 size_t changes(0);
9bedffaa 6991
a54b1c10 6992 [essential_ removeAllObjects];
9bedffaa 6993 [broken_ removeAllObjects];
b4d89997 6994
36bb2ca2 6995 NSArray *packages = [database_ packages];
affeffc7 6996 for (Package *package in packages) {
9bedffaa
JF
6997 if ([package half])
6998 [broken_ addObject:package];
31f3cfff 6999 if ([package upgradableAndEssential:NO]) {
a54b1c10
JF
7000 if ([package essential])
7001 [essential_ addObject:package];
36bb2ca2 7002 ++changes;
a54b1c10 7003 }
36bb2ca2 7004 }
b4d89997 7005
36bb2ca2
JF
7006 if (changes != 0) {
7007 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
c25a610d 7008 [buttonbar_ setBadgeValue:badge forButton:3];
9e98e020
JF
7009 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
7010 [buttonbar_ setBadgeAnimated:YES forButton:3];
c25a610d
JF
7011 [self setApplicationBadge:badge];
7012 } else {
7013 [buttonbar_ setBadgeValue:nil forButton:3];
9e98e020
JF
7014 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
7015 [buttonbar_ setBadgeAnimated:NO forButton:3];
c25a610d
JF
7016 [self removeApplicationBadge];
7017 }
b4d89997 7018
6d9712c4
JF
7019 [self updateData];
7020
c390d3ab 7021#if !ForSaurik
6f1a15d9
JF
7022 if ([packages count] == 0);
7023 else if (Loaded_)
c390d3ab 7024#endif
6d9712c4 7025 [self _loaded];
c390d3ab 7026#if !ForSaurik
6f1a15d9 7027 else {
6d9712c4
JF
7028 Loaded_ = YES;
7029 [book_ update];
6f1a15d9 7030 }
c390d3ab 7031#endif
6d9712c4
JF
7032
7033 /*[hud show:NO];
7034 [hud removeFromSuperview];*/
7035}
7036
7b0ce2da 7037- (void) _saveConfig {
bbb879fb 7038 if (Changed_) {
807ae6d7 7039 _trace();
bbb879fb 7040 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
807ae6d7 7041 _trace();
bbb879fb
JF
7042 Changed_ = false;
7043 }
7b0ce2da
JF
7044}
7045
7046- (void) updateData {
7047 [self _saveConfig];
36bb2ca2 7048
9b619239 7049 /* XXX: this is just stupid */
807ae6d7 7050 if (tag_ != 2 && install_ != nil)
9b619239 7051 [install_ reloadData];
807ae6d7 7052 if (tag_ != 3 && changes_ != nil)
9b619239 7053 [changes_ reloadData];
807ae6d7 7054 if (tag_ != 5 && search_ != nil)
9b619239
JF
7055 [search_ reloadData];
7056
36bb2ca2 7057 [book_ reloadData];
b4d89997
JF
7058}
7059
7b0ce2da
JF
7060- (void) update_ {
7061 [database_ update];
7062}
7063
7064- (void) syncData {
7065 FILE *file = fopen("/etc/apt/sources.list.d/cydia.list", "w");
7066 _assert(file != NULL);
7067
7068 NSArray *keys = [Sources_ allKeys];
7069
7070 for (int i(0), e([keys count]); i != e; ++i) {
7071 NSString *key = [keys objectAtIndex:i];
7072 NSDictionary *source = [Sources_ objectForKey:key];
7073
7074 fprintf(file, "%s %s %s\n",
7075 [[source objectForKey:@"Type"] UTF8String],
7076 [[source objectForKey:@"URI"] UTF8String],
7077 [[source objectForKey:@"Distribution"] UTF8String]
7078 );
7079 }
7080
7081 fclose(file);
7082
7083 [self _saveConfig];
7084
7085 [progress_
7086 detachNewThreadSelector:@selector(update_)
7087 toTarget:self
7088 withObject:nil
baf80942 7089 title:@"Updating Sources"
7b0ce2da
JF
7090 ];
7091}
7092
36bb2ca2
JF
7093- (void) reloadData {
7094 @synchronized (self) {
7095 if (confirm_ == nil)
7096 [self _reloadData];
7097 }
b4d89997
JF
7098}
7099
7100- (void) resolve {
7101 pkgProblemResolver *resolver = [database_ resolver];
7102
7103 resolver->InstallProtect();
7104 if (!resolver->Resolve(true))
7105 _error->Discard();
2367a917
JF
7106}
7107
7108- (void) perform {
8fe19fc1 7109 [database_ prepare];
49048579 7110
affeffc7
JF
7111 confirm_ = [[RVBook alloc] initWithFrame:[underlay_ bounds]];
7112 [confirm_ setDelegate:self];
49048579 7113
affeffc7
JF
7114 ConfirmationView *page([[[ConfirmationView alloc] initWithBook:confirm_ database:database_] autorelease]);
7115 [page setDelegate:self];
49048579 7116
affeffc7
JF
7117 [confirm_ setPage:page];
7118 [underlay_ popSubview:confirm_];
3178d79b
JF
7119}
7120
36bb2ca2
JF
7121- (void) installPackage:(Package *)package {
7122 @synchronized (self) {
7123 [package install];
7124 [self resolve];
7125 [self perform];
7126 }
7127}
7128
7129- (void) removePackage:(Package *)package {
7130 @synchronized (self) {
7131 [package remove];
7132 [self resolve];
7133 [self perform];
7134 }
7135}
7136
7137- (void) distUpgrade {
7138 @synchronized (self) {
7139 [database_ upgrade];
7140 [self perform];
7141 }
b4d89997
JF
7142}
7143
3178d79b 7144- (void) cancel {
36bb2ca2 7145 @synchronized (self) {
36bb2ca2 7146 [self _reloadData];
c390d3ab
JF
7147 if (confirm_ != nil) {
7148 [confirm_ release];
7149 confirm_ = nil;
7150 }
36bb2ca2 7151 }
3178d79b
JF
7152}
7153
7154- (void) confirm {
7155 [overlay_ removeFromSuperview];
77fcccaf 7156 reload_ = true;
3178d79b 7157
2367a917
JF
7158 [progress_
7159 detachNewThreadSelector:@selector(perform)
7160 toTarget:database_
7161 withObject:nil
baf80942 7162 title:@"Running"
2367a917
JF
7163 ];
7164}
7165
b31b87cc
JF
7166- (void) bootstrap_ {
7167 [database_ update];
7168 [database_ upgrade];
7169 [database_ prepare];
7170 [database_ perform];
7171}
7172
7173- (void) bootstrap {
7174 [progress_
7175 detachNewThreadSelector:@selector(bootstrap_)
7176 toTarget:self
7177 withObject:nil
baf80942 7178 title:@"Bootstrap Install"
b31b87cc
JF
7179 ];
7180}
7181
2367a917 7182- (void) progressViewIsComplete:(ProgressView *)progress {
c390d3ab
JF
7183 if (confirm_ != nil) {
7184 [underlay_ addSubview:overlay_];
affeffc7 7185 [confirm_ popFromSuperviewAnimated:NO];
3178d79b 7186 }
c390d3ab
JF
7187
7188 [self cancel];
dc5812ec
JF
7189}
7190
36bb2ca2 7191- (void) setPage:(RVPage *)page {
9b619239 7192 [page resetViewAnimated:NO];
36bb2ca2
JF
7193 [page setDelegate:self];
7194 [book_ setPage:page];
dc5812ec
JF
7195}
7196
7b0ce2da 7197- (RVPage *) _pageForURL:(NSURL *)url withClass:(Class)_class {
770f2a8e 7198 BrowserView *browser = [[[_class alloc] initWithBook:book_] autorelease];
7b0ce2da 7199 [browser loadURL:url];
36bb2ca2 7200 return browser;
2367a917
JF
7201}
7202
7b0ce2da
JF
7203- (void) _setHomePage {
7204 [self setPage:[self _pageForURL:[NSURL URLWithString:@"http://cydia.saurik.com/"] withClass:[HomeView class]]];
7205}
7206
dc5812ec 7207- (void) buttonBarItemTapped:(id)sender {
06aa974d 7208 unsigned tag = [sender tag];
559dec34
JF
7209 if (tag == tag_) {
7210 [book_ resetViewAnimated:YES];
7211 return;
6d9712c4
JF
7212 } else if (tag_ == 2 && tag != 2)
7213 [install_ resetView];
dc5812ec 7214
06aa974d 7215 switch (tag) {
11108eb6 7216 case 1: [self _setHomePage]; break;
36bb2ca2 7217
11108eb6
JF
7218 case 2: [self setPage:install_]; break;
7219 case 3: [self setPage:changes_]; break;
7220 case 4: [self setPage:manage_]; break;
7221 case 5: [self setPage:search_]; break;
36bb2ca2 7222
11108eb6 7223 default: _assert(false);
dc5812ec
JF
7224 }
7225
06aa974d 7226 tag_ = tag;
06aa974d
JF
7227}
7228
b4d89997 7229- (void) applicationWillSuspend {
77fcccaf 7230 [database_ clean];
eeb9b112 7231 [super applicationWillSuspend];
b4d89997
JF
7232}
7233
7b0ce2da 7234- (void) askForSettings {
a9543575 7235 UIActionSheet *role = [[[UIActionSheet alloc]
7b0ce2da
JF
7236 initWithTitle:@"Who Are You?"
7237 buttons:[NSArray arrayWithObjects:
7238 @"User (Graphical Only)",
f641a0e5 7239 @"Hacker (+ Command Line)",
7b0ce2da
JF
7240 @"Developer (No Filters)",
7241 nil]
7242 defaultButtonIndex:-1
7243 delegate:self
7244 context:@"role"
7245 ] autorelease];
7246
7247 [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."];
7248 [role popupAlertAnimated:YES];
7249}
7250
bd150f54
JF
7251- (void) finish {
7252 if (hud_ != nil) {
7253 [self setStatusBarShowsProgress:NO];
a54b1c10 7254
bd150f54
JF
7255 [hud_ show:NO];
7256 [hud_ removeFromSuperview];
7257 [hud_ autorelease];
7258 hud_ = nil;
f9d1e9a0 7259
c6ad32a6
JF
7260 pid_t pid = ExecFork();
7261 if (pid == 0) {
7262 execlp("launchctl", "launchctl", "stop", "com.apple.SpringBoard", NULL);
7263 perror("launchctl stop");
7264 }
7265
f9d1e9a0 7266 return;
bd150f54 7267 }
3178d79b 7268
7b0ce2da
JF
7269 if (Role_ == nil) {
7270 [self askForSettings];
7271 return;
7272 }
7273
3178d79b 7274 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
b31b87cc 7275
bd150f54 7276 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
36bb2ca2
JF
7277 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
7278 0, 0, screenrect.size.width, screenrect.size.height - 48
7279 ) database:database_];
dc5812ec 7280
36bb2ca2 7281 [book_ setDelegate:self];
dc5812ec 7282
36bb2ca2 7283 [overlay_ addSubview:book_];
b19871dd 7284
dc5812ec
JF
7285 NSArray *buttonitems = [NSArray arrayWithObjects:
7286 [NSDictionary dictionaryWithObjectsAndKeys:
7287 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
9b619239
JF
7288 @"home-up.png", kUIButtonBarButtonInfo,
7289 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
7290 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
7291 self, kUIButtonBarButtonTarget,
9b619239 7292 @"Home", kUIButtonBarButtonTitle,
dc5812ec
JF
7293 @"0", kUIButtonBarButtonType,
7294 nil],
7295
7296 [NSDictionary dictionaryWithObjectsAndKeys:
7297 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
7298 @"install-up.png", kUIButtonBarButtonInfo,
7299 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
7300 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
7301 self, kUIButtonBarButtonTarget,
6d9712c4 7302 @"Sections", kUIButtonBarButtonTitle,
dc5812ec
JF
7303 @"0", kUIButtonBarButtonType,
7304 nil],
7305
7306 [NSDictionary dictionaryWithObjectsAndKeys:
7307 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
7308 @"changes-up.png", kUIButtonBarButtonInfo,
7309 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
7310 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
7311 self, kUIButtonBarButtonTarget,
b4d89997 7312 @"Changes", kUIButtonBarButtonTitle,
dc5812ec
JF
7313 @"0", kUIButtonBarButtonType,
7314 nil],
7315
7316 [NSDictionary dictionaryWithObjectsAndKeys:
7317 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
7318 @"manage-up.png", kUIButtonBarButtonInfo,
7319 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
7320 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
7321 self, kUIButtonBarButtonTarget,
36bb2ca2 7322 @"Manage", kUIButtonBarButtonTitle,
dc5812ec
JF
7323 @"0", kUIButtonBarButtonType,
7324 nil],
7325
7326 [NSDictionary dictionaryWithObjectsAndKeys:
7327 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
7328 @"search-up.png", kUIButtonBarButtonInfo,
7329 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
7330 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
7331 self, kUIButtonBarButtonTarget,
b4d89997 7332 @"Search", kUIButtonBarButtonTitle,
dc5812ec
JF
7333 @"0", kUIButtonBarButtonType,
7334 nil],
7335 nil];
7336
a9543575 7337 buttonbar_ = [[UIToolbar alloc]
3178d79b 7338 initInView:overlay_
dc5812ec 7339 withFrame:CGRectMake(
2388b078
JF
7340 0, screenrect.size.height - ButtonBarHeight_,
7341 screenrect.size.width, ButtonBarHeight_
dc5812ec
JF
7342 )
7343 withItemList:buttonitems
7344 ];
7345
2367a917
JF
7346 [buttonbar_ setDelegate:self];
7347 [buttonbar_ setBarStyle:1];
7348 [buttonbar_ setButtonBarTrackingMode:2];
dc5812ec
JF
7349
7350 int buttons[5] = {1, 2, 3, 4, 5};
2367a917
JF
7351 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
7352 [buttonbar_ showButtonGroup:0 withDuration:0];
dc5812ec
JF
7353
7354 for (int i = 0; i != 5; ++i)
2367a917 7355 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
2388b078 7356 i * 64 + 2, 1, 60, ButtonBarHeight_
dc5812ec
JF
7357 )];
7358
2367a917 7359 [buttonbar_ showSelectionForButton:1];
3178d79b 7360 [overlay_ addSubview:buttonbar_];
dc5812ec 7361
b4d89997 7362 [UIKeyboard initImplementationNow];
b4d89997 7363 CGSize keysize = [UIKeyboard defaultSize];
ab398adf 7364 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
b4d89997 7365 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
3622197e 7366 //[[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
ab398adf 7367 [overlay_ addSubview:keyboard_];
b4d89997 7368
807ae6d7
JF
7369 if (!bootstrap_)
7370 [underlay_ addSubview:overlay_];
7371
7372 [self reloadData];
7373
9b619239
JF
7374 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
7375 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
9b619239
JF
7376 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
7377
7b0ce2da
JF
7378 manage_ = (ManageView *) [[self
7379 _pageForURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"manage" ofType:@"html"]]
7380 withClass:[ManageView class]
7381 ] retain];
7382
b31b87cc
JF
7383 if (bootstrap_)
7384 [self bootstrap];
36bb2ca2 7385 else
9b619239 7386 [self _setHomePage];
b4d89997
JF
7387}
7388
a9543575 7389- (void) alertSheet:(UIActionSheet *)sheet buttonClicked:(int)button {
1cedb821
JF
7390 NSString *context([sheet context]);
7391
7392 if ([context isEqualToString:@"fixhalf"]) {
7b0ce2da
JF
7393 switch (button) {
7394 case 1:
7395 @synchronized (self) {
7396 for (int i = 0, e = [broken_ count]; i != e; ++i) {
7397 Package *broken = [broken_ objectAtIndex:i];
7398 [broken remove];
7399
7400 NSString *id = [broken id];
7401 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
7402 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
7403 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
7404 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
7405 }
7406
7407 [self resolve];
7408 [self perform];
7409 }
7410 break;
7411
7412 case 2:
7413 [broken_ removeAllObjects];
7414 [self _loaded];
7415 break;
7416
7417 default:
7418 _assert(false);
7419 }
1cedb821
JF
7420
7421 [sheet dismiss];
7422 } else if ([context isEqualToString:@"role"]) {
7b0ce2da
JF
7423 switch (button) {
7424 case 1: Role_ = @"User"; break;
7425 case 2: Role_ = @"Hacker"; break;
7426 case 3: Role_ = @"Developer"; break;
7427
7428 default:
7429 Role_ = nil;
7430 _assert(false);
7431 }
7432
7433 bool reset = Settings_ != nil;
7434
7435 Settings_ = [NSMutableDictionary dictionaryWithObjectsAndKeys:
7436 Role_, @"Role",
7437 nil];
7438
7439 [Metadata_ setObject:Settings_ forKey:@"Settings"];
7440
7441 Changed_ = true;
7442
7443 if (reset)
7444 [self updateData];
7445 else
7446 [self finish];
1cedb821
JF
7447
7448 [sheet dismiss];
7449 } else if ([context isEqualToString:@"upgrade"]) {
7b0ce2da
JF
7450 switch (button) {
7451 case 1:
7452 @synchronized (self) {
7453 for (int i = 0, e = [essential_ count]; i != e; ++i) {
7454 Package *essential = [essential_ objectAtIndex:i];
7455 [essential install];
7456 }
7457
7458 [self resolve];
7459 [self perform];
7460 }
7461 break;
7462
7463 case 2:
affeffc7
JF
7464 [self distUpgrade];
7465 break;
7466
7467 case 3:
7b0ce2da
JF
7468 Ignored_ = YES;
7469 break;
7470
7471 default:
7472 _assert(false);
7473 }
7474
1cedb821
JF
7475 [sheet dismiss];
7476 }
7b0ce2da
JF
7477}
7478
affeffc7 7479- (void) reorganize { _pooled
e64882de
JF
7480 system("/usr/libexec/cydia/free.sh");
7481 [self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:NO];
bd150f54
JF
7482}
7483
7484- (void) applicationSuspend:(__GSEvent *)event {
7485 if (hud_ == nil && ![progress_ isRunning])
7486 [super applicationSuspend:event];
bd150f54
JF
7487}
7488
6d9712c4
JF
7489- (void) _animateSuspension:(BOOL)arg0 duration:(double)arg1 startTime:(double)arg2 scale:(float)arg3 {
7490 if (hud_ == nil)
7491 [super _animateSuspension:arg0 duration:arg1 startTime:arg2 scale:arg3];
7492}
7493
7494- (void) _setSuspended:(BOOL)value {
7495 if (hud_ == nil)
7496 [super _setSuspended:value];
7497}
7498
7b0ce2da
JF
7499- (UIProgressHUD *) addProgressHUD {
7500 UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
7501 [hud show:YES];
7502 [underlay_ addSubview:hud];
7503 return hud;
7504}
7505
c390d3ab 7506- (void) openMailToURL:(NSURL *)url {
86316a91
JF
7507// XXX: this makes me sad
7508#if 0
c390d3ab 7509 [[[MailToView alloc] initWithView:underlay_ delegate:self url:url] autorelease];
86316a91 7510#else
9fe5e5f8 7511 [UIApp openURL:url];// asPanel:YES];
86316a91 7512#endif
c390d3ab
JF
7513}
7514
6565453a
JF
7515- (void) clearFirstResponder {
7516 if (id responder = [window_ firstResponder])
7517 [responder resignFirstResponder];
7518}
7519
c390d3ab
JF
7520- (RVPage *) pageForPackage:(NSString *)name {
7521 if (Package *package = [database_ packageWithName:name]) {
7522 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
7523 [view setPackage:package];
7524 return view;
7525 } else {
7526 UIActionSheet *sheet = [[[UIActionSheet alloc]
7527 initWithTitle:@"Cannot Locate Package"
7528 buttons:[NSArray arrayWithObjects:@"Close", nil]
7529 defaultButtonIndex:0
7530 delegate:self
7531 context:@"missing"
7532 ] autorelease];
7533
7534 [sheet setBodyText:[NSString stringWithFormat:
7535 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
7536 , name]];
7537
7538 [sheet popupAlertAnimated:YES];
7539 return nil;
7540 }
7541}
7542
7543- (RVPage *) pageForURL:(NSURL *)url hasTag:(int *)tag {
7544 NSString *href = [url absoluteString];
7545
7546 if (tag != NULL)
7547 tag = 0;
7548
7549 if ([href isEqualToString:@"cydia://add-source"])
7550 return [[[AddSourceView alloc] initWithBook:book_ database:database_] autorelease];
7551 else if ([href isEqualToString:@"cydia://sources"])
7552 return [[[SourceTable alloc] initWithBook:book_ database:database_] autorelease];
7553 else if ([href isEqualToString:@"cydia://packages"])
7554 return [[[InstalledView alloc] initWithBook:book_ database:database_] autorelease];
7555 else if ([href hasPrefix:@"cydia://url/"])
7556 return [self _pageForURL:[NSURL URLWithString:[href substringFromIndex:12]] withClass:[BrowserView class]];
affeffc7
JF
7557 else if ([href hasPrefix:@"cydia://launch/"])
7558 [self launchApplicationWithIdentifier:[href substringFromIndex:15] suspended:NO];
807ae6d7
JF
7559 else if ([href hasPrefix:@"cydia://package-settings/"])
7560 return [[[SettingsView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:25]] autorelease];
770f2a8e
JF
7561 else if ([href hasPrefix:@"cydia://package-signature/"])
7562 return [[[SignatureView alloc] initWithBook:book_ database:database_ package:[href substringFromIndex:26]] autorelease];
c390d3ab
JF
7563 else if ([href hasPrefix:@"cydia://package/"])
7564 return [self pageForPackage:[href substringFromIndex:16]];
7565 else if ([href hasPrefix:@"cydia://files/"]) {
7566 NSString *name = [href substringFromIndex:14];
7567
7568 if (Package *package = [database_ packageWithName:name]) {
7569 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
7570 [files setPackage:package];
7571 return files;
7572 }
7573 }
7574
7575 return nil;
7576}
7577
7578- (void) applicationOpenURL:(NSURL *)url {
7579 [super applicationOpenURL:url];
7580 int tag;
7581 if (RVPage *page = [self pageForURL:url hasTag:&tag]) {
7582 [self setPage:page];
7583 [buttonbar_ showSelectionForButton:tag];
7584 tag_ = tag;
7585 }
7586}
7587
bd150f54 7588- (void) applicationDidFinishLaunching:(id)unused {
f641a0e5
JF
7589 Font12_ = [[UIFont systemFontOfSize:12] retain];
7590 Font12Bold_ = [[UIFont boldSystemFontOfSize:12] retain];
7591 Font14_ = [[UIFont systemFontOfSize:14] retain];
7592 Font18Bold_ = [[UIFont boldSystemFontOfSize:18] retain];
7593 Font22Bold_ = [[UIFont boldSystemFontOfSize:22] retain];
7594
bd150f54
JF
7595 _assert(pkgInitConfig(*_config));
7596 _assert(pkgInitSystem(*_config, _system));
7597
bd150f54
JF
7598 tag_ = 1;
7599
7600 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
7601 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
7602
770f2a8e
JF
7603 [NSURLProtocol registerClass:[CydiaURLProtocol class]];
7604
bd150f54
JF
7605 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
7606 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
7607
f641a0e5
JF
7608 [window_ orderFront:self];
7609 [window_ makeKey:self];
c390d3ab 7610 [window_ setHidden:NO];
bd150f54 7611
770f2a8e 7612 database_ = [Database sharedInstance];
bd150f54
JF
7613 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
7614 [database_ setDelegate:progress_];
7615 [window_ setContentView:progress_];
7616
7617 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
7618 [progress_ setContentView:underlay_];
7619
7620 [progress_ resetView];
7621
6d9712c4 7622 if (
bd150f54 7623 readlink("/Applications", NULL, 0) == -1 && errno == EINVAL ||
bd150f54 7624 readlink("/Library/Ringtones", NULL, 0) == -1 && errno == EINVAL ||
6d9712c4
JF
7625 readlink("/Library/Wallpaper", NULL, 0) == -1 && errno == EINVAL ||
7626 readlink("/usr/include", NULL, 0) == -1 && errno == EINVAL ||
7627 readlink("/usr/libexec", NULL, 0) == -1 && errno == EINVAL ||
90515f88
JF
7628 readlink("/usr/share", NULL, 0) == -1 && errno == EINVAL /*||
7629 readlink("/var/lib", NULL, 0) == -1 && errno == EINVAL*/
bd150f54 7630 ) {
5afbb4b7
JF
7631 [self setIdleTimerDisabled:YES];
7632
7b0ce2da 7633 hud_ = [self addProgressHUD];
1cedb821 7634 [hud_ setText:@"Reorganizing\n\nWill Automatically\nClose When Done"];
bd150f54
JF
7635
7636 [self setStatusBarShowsProgress:YES];
7637
7638 [NSThread
7639 detachNewThreadSelector:@selector(reorganize)
7640 toTarget:self
7641 withObject:nil
7642 ];
6d9712c4 7643 } else
bd150f54
JF
7644 [self finish];
7645}
7646
b4d89997 7647- (void) showKeyboard:(BOOL)show {
ab398adf
JF
7648 CGSize keysize = [UIKeyboard defaultSize];
7649 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
7650 CGRect keyup = keydown;
7651 keyup.origin.y -= keysize.height;
7652
7653 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
7654 [animation setSignificantRectFields:2];
7655
7656 if (show) {
7657 [animation setStartFrame:keydown];
7658 [animation setEndFrame:keyup];
7659 [keyboard_ activate];
7660 } else {
7661 [animation setStartFrame:keyup];
7662 [animation setEndFrame:keydown];
7663 [keyboard_ deactivate];
7664 }
7665
7666 [[UIAnimator sharedAnimator]
7667 addAnimations:[NSArray arrayWithObjects:animation, nil]
2388b078 7668 withDuration:KeyboardTime_
ab398adf
JF
7669 start:YES
7670 ];
dc5812ec
JF
7671}
7672
a9543575 7673- (void) slideUp:(UIActionSheet *)alert {
77fcccaf
JF
7674 if (Advanced_)
7675 [alert presentSheetFromButtonBar:buttonbar_];
7676 else
7677 [alert presentSheetInView:overlay_];
36bb2ca2
JF
7678}
7679
dc5812ec
JF
7680@end
7681
affeffc7 7682void AddPreferences(NSString *plist) { _pooled
b4d89997
JF
7683 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
7684 _assert(settings != NULL);
7685 NSMutableArray *items = [settings objectForKey:@"items"];
7686
7687 bool cydia(false);
7688
7689 for (size_t i(0); i != [items count]; ++i) {
7690 NSMutableDictionary *item([items objectAtIndex:i]);
7691 NSString *label = [item objectForKey:@"label"];
7692 if (label != nil && [label isEqualToString:@"Cydia"]) {
7693 cydia = true;
7694 break;
7695 }
7696 }
7697
7698 if (!cydia) {
7699 for (size_t i(0); i != [items count]; ++i) {
7700 NSDictionary *item([items objectAtIndex:i]);
7701 NSString *label = [item objectForKey:@"label"];
7702 if (label != nil && [label isEqualToString:@"General"]) {
7703 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
7704 @"CydiaSettings", @"bundle",
7705 @"PSLinkCell", @"cell",
7706 [NSNumber numberWithBool:YES], @"hasIcon",
7707 [NSNumber numberWithBool:YES], @"isController",
7708 @"Cydia", @"label",
7709 nil] atIndex:(i + 1)];
7710
7711 break;
7712 }
7713 }
7714
7715 _assert([settings writeToFile:plist atomically:YES] == YES);
7716 }
b4d89997
JF
7717}
7718
7719/*IMP alloc_;
7720id Alloc_(id self, SEL selector) {
7721 id object = alloc_(self, selector);
c390d3ab 7722 lprintf("[%s]A-%p\n", self->isa->name, object);
b4d89997
JF
7723 return object;
7724}*/
7725
36bb2ca2
JF
7726/*IMP dealloc_;
7727id Dealloc_(id self, SEL selector) {
7728 id object = dealloc_(self, selector);
c390d3ab 7729 lprintf("[%s]D-%p\n", self->isa->name, object);
36bb2ca2
JF
7730 return object;
7731}*/
b4d89997 7732
affeffc7 7733int main(int argc, char *argv[]) { _pooled
680eb135
JF
7734 class_addMethod(objc_getClass("DOMNodeList"), @selector(countByEnumeratingWithState:objects:count:), (IMP) &DOMNodeList$countByEnumeratingWithState$objects$count$, "I20@0:4^{NSFastEnumerationState}8^@12I16");
7735
de3b1ab4
JF
7736 bool substrate(false);
7737
7738 if (argc != 0) {
7739 char **args(argv);
7740 int arge(1);
7741
7742 for (int argi(1); argi != argc; ++argi)
7743 if (strcmp(argv[argi], "--") == 0) {
7744 arge = argi;
7745 argv[argi] = argv[0];
7746 argv += argi;
7747 argc -= argi;
7748 break;
7749 }
7750
7751 for (int argi(1); argi != arge; ++argi)
7752 if (strcmp(args[argi], "--bootstrap") == 0)
7753 bootstrap_ = true;
7754 else if (strcmp(args[argi], "--substrate") == 0)
7755 substrate = true;
7756 else
7757 fprintf(stderr, "unknown argument: %s\n", args[argi]);
7758 }
b31b87cc 7759
7d2ac47f 7760 App_ = [[NSBundle mainBundle] bundlePath];
d73cede2 7761 Home_ = NSHomeDirectory();
87f46a96 7762 Locale_ = CFLocaleCopyCurrent();
d73cede2
JF
7763
7764 {
7765 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
7766 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
7767 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
7768 Sounds_Keyboard_ = [keyboard boolValue];
7769 }
7770
b4d89997
JF
7771 setuid(0);
7772 setgid(0);
7773
bf8476c8 7774#if 1 /* XXX: this costs 1.4s of startup performance */
aa629131
JF
7775 if (unlink("/var/cache/apt/pkgcache.bin") == -1)
7776 _assert(errno == ENOENT);
7777 if (unlink("/var/cache/apt/srcpkgcache.bin") == -1)
7778 _assert(errno == ENOENT);
9487f027 7779#endif
aa5e5990 7780
b4d89997
JF
7781 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
7782 alloc_ = alloc->method_imp;
7783 alloc->method_imp = (IMP) &Alloc_;*/
7784
36bb2ca2
JF
7785 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
7786 dealloc_ = dealloc->method_imp;
7787 dealloc->method_imp = (IMP) &Dealloc_;*/
7788
3178d79b 7789 size_t size;
c390d3ab
JF
7790
7791 int maxproc;
7792 size = sizeof(maxproc);
7793 if (sysctlbyname("kern.maxproc", &maxproc, &size, NULL, 0) == -1)
7794 perror("sysctlbyname(\"kern.maxproc\", ?)");
7795 else if (maxproc < 64) {
7796 maxproc = 64;
7797 if (sysctlbyname("kern.maxproc", NULL, NULL, &maxproc, sizeof(maxproc)) == -1)
7798 perror("sysctlbyname(\"kern.maxproc\", #)");
7799 }
7800
3178d79b
JF
7801 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
7802 char *machine = new char[size];
c390d3ab
JF
7803 if (sysctlbyname("hw.machine", machine, &size, NULL, 0) == -1)
7804 perror("sysctlbyname(\"hw.machine\", ?)");
7805 else
7806 Machine_ = machine;
3178d79b 7807
87f46a96 7808 UniqueID_ = [[UIDevice currentDevice] uniqueIdentifier];
3178d79b 7809
99364ec4
JF
7810 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
7811 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
b4d89997 7812
680eb135
JF
7813 if ((Indices_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/indices.plist"]) == NULL)
7814 Indices_ = [[NSMutableDictionary alloc] init];
7815
b4d89997
JF
7816 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
7817 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
6d9712c4 7818 else {
7b0ce2da
JF
7819 Settings_ = [Metadata_ objectForKey:@"Settings"];
7820
b4d89997 7821 Packages_ = [Metadata_ objectForKey:@"Packages"];
6d9712c4 7822 Sections_ = [Metadata_ objectForKey:@"Sections"];
7b0ce2da
JF
7823 Sources_ = [Metadata_ objectForKey:@"Sources"];
7824 }
7825
7826 if (Settings_ != nil)
7827 Role_ = [Settings_ objectForKey:@"Role"];
7828
7829 if (Packages_ == nil) {
7830 Packages_ = [[[NSMutableDictionary alloc] initWithCapacity:128] autorelease];
7831 [Metadata_ setObject:Packages_ forKey:@"Packages"];
7832 }
7833
7834 if (Sections_ == nil) {
7835 Sections_ = [[[NSMutableDictionary alloc] initWithCapacity:32] autorelease];
7836 [Metadata_ setObject:Sections_ forKey:@"Sections"];
7837 }
7838
7839 if (Sources_ == nil) {
7840 Sources_ = [[[NSMutableDictionary alloc] initWithCapacity:0] autorelease];
7841 [Metadata_ setObject:Sources_ forKey:@"Sources"];
6d9712c4 7842 }
b4d89997 7843
affeffc7 7844#if RecycleWebViews
baf80942 7845 Documents_ = [[[NSMutableArray alloc] initWithCapacity:4] autorelease];
affeffc7 7846#endif
baf80942 7847
de3b1ab4 7848 if (substrate && access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) == 0)
dddbc481
JF
7849 dlopen("/Library/MobileSubstrate/MobileSubstrate.dylib", RTLD_LAZY | RTLD_GLOBAL);
7850
26c2dd8c
JF
7851 if (access("/User", F_OK) != 0)
7852 system("/usr/libexec/cydia/firmware.sh");
9e98e020 7853
87f46a96
JF
7854 _assert([[NSFileManager defaultManager]
7855 createDirectoryAtPath:@"/var/cache/apt/archives/partial"
7856 withIntermediateDirectories:YES
7857 attributes:nil
7858 error:NULL
7859 ]);
7860
36bb2ca2
JF
7861 space_ = CGColorSpaceCreateDeviceRGB();
7862
f641a0e5 7863 Blue_.Set(space_, 0.2, 0.2, 1.0, 1.0);
77fcccaf 7864 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
36bb2ca2 7865 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
baf80942 7866 Off_.Set(space_, 0.9, 0.9, 0.9, 1.0);
36bb2ca2 7867 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
7b0ce2da
JF
7868 Gray_.Set(space_, 0.4, 0.4, 0.4, 1.0);
7869
bc8cd583
JF
7870 Finishes_ = [NSArray arrayWithObjects:@"return", @"reopen", @"restart", @"reload", @"reboot", nil];
7871
46dbfd32 7872 SectionMap_ = [[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Sections" ofType:@"plist"]] autorelease];
7b0ce2da 7873
87f46a96
JF
7874 UIApplicationUseLegacyEvents(YES);
7875 UIKeyboardDisableAutomaticAppearance();
7876
a9543575 7877 int value = UIApplicationMain(argc, argv, @"Cydia", @"Cydia");
36bb2ca2
JF
7878
7879 CGColorSpaceRelease(space_);
199d0ba5 7880 CFRelease(Locale_);
36bb2ca2 7881
36bb2ca2 7882 return value;
dc5812ec 7883}