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