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