]> git.saurik.com Git - cydia.git/blame - Cydia.mm
UICaboodle could use _packed.
[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_;
2367a917
JF
2160}
2161
36bb2ca2 2162- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2367a917 2163
8b29f8e6 2164- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate;
36bb2ca2
JF
2165- (void) setContentView:(UIView *)view;
2166- (void) resetView;
2367a917 2167
36bb2ca2
JF
2168- (void) _retachThread;
2169- (void) _detachNewThreadData:(ProgressData *)data;
2170- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2367a917
JF
2171
2172@end
2173
36bb2ca2
JF
2174@protocol ProgressViewDelegate
2175- (void) progressViewIsComplete:(ProgressView *)sender;
2176@end
2177
2178@implementation ProgressView
2367a917
JF
2179
2180- (void) dealloc {
36bb2ca2
JF
2181 [transition_ setDelegate:nil];
2182 [navbar_ setDelegate:nil];
2183
2184 [view_ release];
2185 if (background_ != nil)
2186 [background_ release];
2187 [transition_ release];
2188 [overlay_ release];
2189 [navbar_ release];
2190 [progress_ release];
2191 [output_ release];
2192 [status_ release];
31f3cfff 2193 [close_ release];
2367a917
JF
2194 [super dealloc];
2195}
2196
36bb2ca2
JF
2197- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2198 if (bootstrap_ && from == overlay_ && to == view_)
2199 exit(0);
2200}
2367a917 2201
8b29f8e6 2202- (id) initWithFrame:(struct CGRect)frame database:(Database *)database delegate:(id)delegate {
36bb2ca2 2203 if ((self = [super initWithFrame:frame]) != nil) {
8b29f8e6 2204 database_ = database;
36bb2ca2 2205 delegate_ = delegate;
ec97ef06 2206
36bb2ca2
JF
2207 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2208 [transition_ setDelegate:self];
2367a917 2209
36bb2ca2 2210 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
ec97ef06 2211
36bb2ca2
JF
2212 if (bootstrap_)
2213 [overlay_ setBackgroundColor:Black_];
2214 else {
2215 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2216 [background_ setBackgroundColor:Black_];
2217 [self addSubview:background_];
2218 }
ec97ef06 2219
36bb2ca2 2220 [self addSubview:transition_];
2367a917 2221
36bb2ca2
JF
2222 CGSize navsize = [UINavigationBar defaultSize];
2223 CGRect navrect = {{0, 0}, navsize};
2367a917 2224
36bb2ca2
JF
2225 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2226 [overlay_ addSubview:navbar_];
b4d89997 2227
36bb2ca2
JF
2228 [navbar_ setBarStyle:1];
2229 [navbar_ setDelegate:self];
2367a917 2230
36bb2ca2
JF
2231 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2232 [navbar_ pushNavigationItem:navitem];
c7c6384e 2233
36bb2ca2
JF
2234 CGRect bounds = [overlay_ bounds];
2235 CGSize prgsize = [UIProgressBar defaultSize];
ec97ef06 2236
36bb2ca2
JF
2237 CGRect prgrect = {{
2238 (bounds.size.width - prgsize.width) / 2,
2239 bounds.size.height - prgsize.height - 20
2240 }, prgsize};
ec97ef06 2241
36bb2ca2 2242 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
31f3cfff 2243 [progress_ setStyle:0];
ec97ef06 2244
36bb2ca2
JF
2245 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2246 10,
2247 bounds.size.height - prgsize.height - 50,
2248 bounds.size.width - 20,
2249 24
2250 )];
ec97ef06 2251
36bb2ca2
JF
2252 [status_ setColor:White_];
2253 [status_ setBackgroundColor:Clear_];
ec97ef06 2254
36bb2ca2
JF
2255 [status_ setCentersHorizontally:YES];
2256 //[status_ setFont:font];
ec97ef06 2257
36bb2ca2
JF
2258 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2259 10,
2260 navrect.size.height + 20,
2261 bounds.size.width - 20,
2262 bounds.size.height - navsize.height - 62 - navrect.size.height
2263 )];
1d80f6b9 2264
36bb2ca2
JF
2265 //[output_ setTextFont:@"Courier New"];
2266 [output_ setTextSize:12];
2367a917 2267
36bb2ca2
JF
2268 [output_ setTextColor:White_];
2269 [output_ setBackgroundColor:Clear_];
2367a917 2270
36bb2ca2
JF
2271 [output_ setMarginTop:0];
2272 [output_ setAllowsRubberBanding:YES];
795d26fc 2273 [output_ setEditable:NO];
b4d89997 2274
36bb2ca2 2275 [overlay_ addSubview:output_];
2367a917 2276
31f3cfff
JF
2277 close_ = [[UIPushButton alloc] initWithFrame:CGRectMake(
2278 10,
2279 bounds.size.height - prgsize.height - 50,
2280 bounds.size.width - 20,
795d26fc 2281 32 + prgsize.height
31f3cfff
JF
2282 )];
2283
2284 [close_ setAutosizesToFit:NO];
2285 [close_ setDrawsShadow:YES];
2286 [close_ setStretchBackground:YES];
2287 [close_ setTitle:@"Close Window"];
2288 [close_ setEnabled:YES];
2289
2290 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2291 [close_ setTitleFont:bold];
2292 CFRelease(bold);
2293
2294 [close_ addTarget:self action:@selector(closeButtonPushed) forEvents:kUIControlEventMouseUpInside];
2295 [close_ setBackground:[UIImage applicationImageNamed:@"green-up.png"] forState:0];
2296 [close_ setBackground:[UIImage applicationImageNamed:@"green-dn.png"] forState:1];
36bb2ca2 2297 } return self;
2367a917
JF
2298}
2299
36bb2ca2
JF
2300- (void) setContentView:(UIView *)view {
2301 view_ = [view retain];
2367a917
JF
2302}
2303
36bb2ca2
JF
2304- (void) resetView {
2305 [transition_ transition:6 toView:view_];
2367a917
JF
2306}
2307
36bb2ca2 2308- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
8b29f8e6
JF
2309 NSString *context = [sheet context];
2310 if ([context isEqualToString:@"conffile"]) {
2311 FILE *input = [database_ input];
2312
2313 switch (button) {
2314 case 1:
2315 fprintf(input, "N\n");
2316 fflush(input);
9bedffaa 2317 break;
8b29f8e6
JF
2318 case 2:
2319 fprintf(input, "Y\n");
2320 fflush(input);
9bedffaa
JF
2321 break;
2322 default:
2323 _assert(false);
8b29f8e6
JF
2324 }
2325 }
2326
36bb2ca2 2327 [sheet dismiss];
2367a917
JF
2328}
2329
31f3cfff 2330- (void) closeButtonPushed {
36bb2ca2
JF
2331 [delegate_ progressViewIsComplete:self];
2332 [self resetView];
b19871dd
JF
2333}
2334
31f3cfff 2335- (void) _retachThread {
795d26fc
JF
2336 UINavigationItem *item = [navbar_ topItem];
2337 [item setTitle:@"Complete"];
2338
31f3cfff
JF
2339 [overlay_ addSubview:close_];
2340 [progress_ removeFromSuperview];
2341 [status_ removeFromSuperview];
2342}
2343
36bb2ca2 2344- (void) _detachNewThreadData:(ProgressData *)data {
b4d89997
JF
2345 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2346
36bb2ca2
JF
2347 [[data target] performSelector:[data selector] withObject:[data object]];
2348 [data release];
b19871dd 2349
36bb2ca2 2350 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
b19871dd 2351
b4d89997 2352 [pool release];
b19871dd
JF
2353}
2354
36bb2ca2 2355- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
5a09ae08
JF
2356 UINavigationItem *item = [navbar_ topItem];
2357 [item setTitle:title];
b4d89997 2358
36bb2ca2
JF
2359 [status_ setText:nil];
2360 [output_ setText:@""];
2361 [progress_ setProgress:0];
b19871dd 2362
31f3cfff
JF
2363 [close_ removeFromSuperview];
2364 [overlay_ addSubview:progress_];
2365 [overlay_ addSubview:status_];
2366
36bb2ca2 2367 [transition_ transition:6 toView:overlay_];
b19871dd 2368
36bb2ca2
JF
2369 [NSThread
2370 detachNewThreadSelector:@selector(_detachNewThreadData:)
2371 toTarget:self
2372 withObject:[[ProgressData alloc]
2373 initWithSelector:selector
2374 target:target
2375 object:object
2376 ]
2377 ];
b19871dd
JF
2378}
2379
5a09ae08
JF
2380- (void) repairWithSelector:(SEL)selector {
2381 [self
2382 detachNewThreadSelector:selector
2383 toTarget:database_
2384 withObject:nil
2385 title:@"Repairing..."
2386 ];
2387}
2388
8b29f8e6
JF
2389- (void) setConfigurationData:(NSString *)data {
2390 [self
2391 performSelectorOnMainThread:@selector(_setConfigurationData:)
2392 withObject:data
2393 waitUntilDone:YES
2394 ];
2395}
2396
31f3cfff
JF
2397- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
2398 Package *package = id == nil ? nil : [database_ packageWithName:id];
2399
2400 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2401 initWithTitle:(package == nil ? @"Source Error" : [package name])
2402 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2403 defaultButtonIndex:0
2404 delegate:self
2405 context:@"error"
2406 ] autorelease];
2407
2408 [sheet setBodyText:error];
2409 [sheet popupAlertAnimated:YES];
b19871dd
JF
2410}
2411
36bb2ca2
JF
2412- (void) setProgressTitle:(NSString *)title {
2413 [self
2414 performSelectorOnMainThread:@selector(_setProgressTitle:)
2415 withObject:title
2416 waitUntilDone:YES
2417 ];
b19871dd
JF
2418}
2419
36bb2ca2
JF
2420- (void) setProgressPercent:(float)percent {
2421 [self
2422 performSelectorOnMainThread:@selector(_setProgressPercent:)
2423 withObject:[NSNumber numberWithFloat:percent]
2424 waitUntilDone:YES
2425 ];
b19871dd
JF
2426}
2427
36bb2ca2
JF
2428- (void) addProgressOutput:(NSString *)output {
2429 [self
2430 performSelectorOnMainThread:@selector(_addProgressOutput:)
2431 withObject:output
2432 waitUntilDone:YES
2433 ];
b19871dd
JF
2434}
2435
8b29f8e6
JF
2436- (void) _setConfigurationData:(NSString *)data {
2437 _assert(conffile_r(data));
2438
2439 NSString *ofile = conffile_r[1];
2440 //NSString *nfile = conffile_r[2];
2441
2442 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2443 initWithTitle:@"Configuration Upgrade"
2444 buttons:[NSArray arrayWithObjects:
2445 @"Keep My Old Copy",
2446 @"Accept The New Copy",
2447 // XXX: @"See What Changed",
2448 nil]
2449 defaultButtonIndex:0
2450 delegate:self
2451 context:@"conffile"
2452 ] autorelease];
2453
2454 [sheet setBodyText:[NSString stringWithFormat:
2455 @"The following file has been changed by both the package maintainer and by you (or for you by a script).\n\n%@"
2456 , ofile]];
2457
2458 [sheet popupAlertAnimated:YES];
2459}
2460
36bb2ca2
JF
2461- (void) _setProgressTitle:(NSString *)title {
2462 [status_ setText:[title stringByAppendingString:@"..."]];
b4d89997 2463}
b19871dd 2464
36bb2ca2
JF
2465- (void) _setProgressPercent:(NSNumber *)percent {
2466 [progress_ setProgress:[percent floatValue]];
49048579
JF
2467}
2468
36bb2ca2
JF
2469- (void) _addProgressOutput:(NSString *)output {
2470 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2471 CGSize size = [output_ contentSize];
2472 CGRect rect = {{0, size.height}, {size.width, 0}};
2473 [output_ scrollRectToVisible:rect animated:YES];
2474}
dc088e63 2475
36bb2ca2
JF
2476@end
2477/* }}} */
dc088e63 2478
36bb2ca2
JF
2479/* Package Cell {{{ */
2480@interface PackageCell : UITableCell {
2481 UIImageView *icon_;
2482 UITextLabel *name_;
2483 UITextLabel *description_;
2484 UITextLabel *source_;
9bedffaa 2485 //UIImageView *trusted_;
31f3cfff 2486#ifdef USE_BADGES
9bedffaa
JF
2487 UIImageView *badge_;
2488 UITextLabel *status_;
31f3cfff
JF
2489#endif
2490 BOOL setup_;
36bb2ca2 2491}
723a0072 2492
36bb2ca2
JF
2493- (PackageCell *) init;
2494- (void) setPackage:(Package *)package;
ec97ef06 2495
36bb2ca2
JF
2496- (void) _setSelected:(float)fraction;
2497- (void) setSelected:(BOOL)selected;
2498- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2499- (void) _setSelectionFadeFraction:(float)fraction;
ec97ef06 2500
9bedffaa
JF
2501+ (int) heightForPackage:(Package *)package;
2502
36bb2ca2 2503@end
49048579 2504
36bb2ca2
JF
2505@implementation PackageCell
2506
2507- (void) dealloc {
2508 [icon_ release];
2509 [name_ release];
2510 [description_ release];
2511 [source_ release];
31f3cfff 2512#ifdef USE_BADGES
9bedffaa
JF
2513 [badge_ release];
2514 [status_ release];
31f3cfff 2515#endif
9bedffaa 2516 //[trusted_ release];
36bb2ca2 2517 [super dealloc];
b4d89997 2518}
b19871dd 2519
36bb2ca2
JF
2520- (PackageCell *) init {
2521 if ((self = [super init]) != nil) {
2522 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2523 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2524 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
b19871dd 2525
36bb2ca2 2526 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
b19871dd 2527
36bb2ca2
JF
2528 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2529 [name_ setBackgroundColor:Clear_];
2530 [name_ setFont:bold];
b19871dd 2531
36bb2ca2
JF
2532 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2533 [source_ setBackgroundColor:Clear_];
2534 [source_ setFont:large];
b19871dd 2535
36bb2ca2
JF
2536 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2537 [description_ setBackgroundColor:Clear_];
2538 [description_ setFont:small];
b19871dd 2539
9bedffaa
JF
2540 /*trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2541 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];*/
2542
31f3cfff 2543#ifdef USE_BADGES
9bedffaa
JF
2544 badge_ = [[UIImageView alloc] initWithFrame:CGRectMake(17, 70, 16, 16)];
2545
2546 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 68, 280, 20)];
2547 [status_ setBackgroundColor:Clear_];
2548 [status_ setFont:small];
31f3cfff
JF
2549#endif
2550
2551 /*[icon_ setImage:[UIImage applicationImageNamed:@"unknown.png"]];
2552 [icon_ zoomToScale:0.5];
2553 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];*/
b19871dd 2554
36bb2ca2
JF
2555 [self addSubview:icon_];
2556 [self addSubview:name_];
2557 [self addSubview:description_];
2558 [self addSubview:source_];
2559
2560 CFRelease(small);
2561 CFRelease(large);
2562 CFRelease(bold);
2563 } return self;
b4d89997 2564}
b19871dd 2565
36bb2ca2 2566- (void) setPackage:(Package *)package {
31f3cfff
JF
2567 /*if (setup_)
2568 return;
2569 else
2570 setup_ = YES;*/
2571
36bb2ca2 2572 Source *source = [package source];
b19871dd 2573
36bb2ca2
JF
2574 UIImage *image = nil;
2575 if (NSString *icon = [package icon])
2576 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2577 if (image == nil) if (NSString *icon = [source defaultIcon])
2578 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2579 if (image == nil)
2580 image = [UIImage applicationImageNamed:@"unknown.png"];
36bb2ca2 2581 [icon_ setImage:image];
a0be02eb 2582
31f3cfff 2583 /*if (image != nil) {
a0be02eb
JF
2584 CGSize size = [image size];
2585 float scale = 30 / std::max(size.width, size.height);
2586 [icon_ zoomToScale:scale];
31f3cfff 2587 }*/
a0be02eb 2588
36bb2ca2 2589 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
b19871dd 2590
36bb2ca2
JF
2591 [name_ setText:[package name]];
2592 [description_ setText:[package tagline]];
2593
a54b1c10
JF
2594 NSString *label = nil;
2595 bool trusted = false;
b19871dd 2596
36bb2ca2
JF
2597 if (source != nil) {
2598 label = [source label];
2599 trusted = [source trusted];
a54b1c10 2600 } else if ([[package id] isEqualToString:@"firmware"])
36bb2ca2 2601 label = @"Apple";
a54b1c10
JF
2602
2603 if (label == nil)
36bb2ca2 2604 label = @"Unknown/Local";
b19871dd 2605
a54b1c10
JF
2606 NSString *from = [NSString stringWithFormat:@"from %@", label];
2607
2608 NSString *section = Simplify([package section]);
2609 if (section != nil && ![section isEqualToString:label])
2610 from = [from stringByAppendingString:[NSString stringWithFormat:@" (%@)", section]];
2611
2612 [source_ setText:from];
36bb2ca2 2613
31f3cfff
JF
2614#ifdef USE_BADGES
2615 [badge_ removeFromSuperview];
2616 [status_ removeFromSuperview];
2617
9bedffaa
JF
2618 if (NSString *mode = [package mode]) {
2619 [badge_ setImage:[UIImage applicationImageNamed:
2620 [mode isEqualToString:@"Remove"] || [mode isEqualToString:@"Purge"] ? @"removing.png" : @"installing.png"
2621 ]];
2622
2623 [status_ setText:[NSString stringWithFormat:@"Queued for %@", mode]];
2624 [status_ setColor:Blueish_];
2625 } else if ([package half]) {
2626 [badge_ setImage:[UIImage applicationImageNamed:@"damaged.png"]];
2627 [status_ setText:@"Package Damaged"];
2628 [status_ setColor:Red_];
2629 } else {
2630 [badge_ setImage:nil];
2631 [status_ setText:nil];
31f3cfff 2632 goto done;
9bedffaa 2633 }
31f3cfff
JF
2634
2635 [self addSubview:badge_];
2636 [self addSubview:status_];
2637 done:;
2638#endif
b19871dd
JF
2639}
2640
36bb2ca2
JF
2641- (void) _setSelected:(float)fraction {
2642 CGColor black(space_,
2643 Interpolate(0.0, 1.0, fraction),
2644 Interpolate(0.0, 1.0, fraction),
2645 Interpolate(0.0, 1.0, fraction),
2646 1.0);
b19871dd 2647
36bb2ca2
JF
2648 CGColor gray(space_,
2649 Interpolate(0.4, 1.0, fraction),
2650 Interpolate(0.4, 1.0, fraction),
2651 Interpolate(0.4, 1.0, fraction),
2652 1.0);
b19871dd 2653
36bb2ca2
JF
2654 [name_ setColor:black];
2655 [description_ setColor:gray];
2656 [source_ setColor:black];
b19871dd
JF
2657}
2658
36bb2ca2
JF
2659- (void) setSelected:(BOOL)selected {
2660 [self _setSelected:(selected ? 1.0 : 0.0)];
2661 [super setSelected:selected];
b19871dd
JF
2662}
2663
36bb2ca2
JF
2664- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2665 if (!fade)
2666 [self _setSelected:(selected ? 1.0 : 0.0)];
2667 [super setSelected:selected withFade:fade];
2668}
2669
2670- (void) _setSelectionFadeFraction:(float)fraction {
2671 [self _setSelected:fraction];
2672 [super _setSelectionFadeFraction:fraction];
ec97ef06
JF
2673}
2674
9bedffaa 2675+ (int) heightForPackage:(Package *)package {
31f3cfff 2676#ifdef USE_BADGES
9bedffaa
JF
2677 if ([package hasMode] || [package half])
2678 return 96;
2679 else
31f3cfff 2680#endif
9bedffaa
JF
2681 return 73;
2682}
2683
8da60fb7
JF
2684@end
2685/* }}} */
36bb2ca2
JF
2686/* Section Cell {{{ */
2687@interface SectionCell : UITableCell {
2688 UITextLabel *name_;
2689 UITextLabel *count_;
b4d89997 2690}
b19871dd 2691
36bb2ca2
JF
2692- (id) init;
2693- (void) setSection:(Section *)section;
2694
2695- (void) _setSelected:(float)fraction;
2696- (void) setSelected:(BOOL)selected;
2697- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2698- (void) _setSelectionFadeFraction:(float)fraction;
b19871dd 2699
8da60fb7
JF
2700@end
2701
36bb2ca2 2702@implementation SectionCell
8da60fb7 2703
36bb2ca2
JF
2704- (void) dealloc {
2705 [name_ release];
2706 [count_ release];
2707 [super dealloc];
2708}
2709
2710- (id) init {
b4d89997 2711 if ((self = [super init]) != nil) {
36bb2ca2
JF
2712 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2713 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2714
2715 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2716 [name_ setBackgroundColor:Clear_];
2717 [name_ setFont:bold];
2718
2719 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2720 [count_ setCentersHorizontally:YES];
2721 [count_ setBackgroundColor:Clear_];
2722 [count_ setFont:small];
2723 [count_ setColor:White_];
2724
2725 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2726 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2727
2728 [self addSubview:folder];
2729 [self addSubview:name_];
2730 [self addSubview:count_];
2731
2732 [self _setSelected:0];
2733
2734 CFRelease(small);
2735 CFRelease(bold);
b4d89997 2736 } return self;
b19871dd
JF
2737}
2738
36bb2ca2
JF
2739- (void) setSection:(Section *)section {
2740 if (section == nil) {
2741 [name_ setText:@"All Packages"];
2742 [count_ setText:nil];
2743 } else {
2744 NSString *name = [section name];
2745 [name_ setText:(name == nil ? @"(No Section)" : name)];
2746 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2747 }
b19871dd
JF
2748}
2749
36bb2ca2
JF
2750- (void) _setSelected:(float)fraction {
2751 CGColor black(space_,
2752 Interpolate(0.0, 1.0, fraction),
2753 Interpolate(0.0, 1.0, fraction),
2754 Interpolate(0.0, 1.0, fraction),
2755 1.0);
b19871dd 2756
36bb2ca2 2757 [name_ setColor:black];
b19871dd
JF
2758}
2759
36bb2ca2
JF
2760- (void) setSelected:(BOOL)selected {
2761 [self _setSelected:(selected ? 1.0 : 0.0)];
2762 [super setSelected:selected];
b19871dd
JF
2763}
2764
36bb2ca2
JF
2765- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2766 if (!fade)
2767 [self _setSelected:(selected ? 1.0 : 0.0)];
2768 [super setSelected:selected withFade:fade];
2769}
b19871dd 2770
36bb2ca2
JF
2771- (void) _setSelectionFadeFraction:(float)fraction {
2772 [self _setSelected:fraction];
2773 [super _setSelectionFadeFraction:fraction];
2774}
b31b87cc 2775
36bb2ca2
JF
2776@end
2777/* }}} */
b19871dd 2778
ab398adf
JF
2779/* File Table {{{ */
2780@interface FileTable : RVPage {
36bb2ca2 2781 _transient Database *database_;
ab398adf
JF
2782 Package *package_;
2783 NSString *name_;
2784 NSMutableArray *files_;
2785 UITable *list_;
2786}
b19871dd 2787
ab398adf
JF
2788- (id) initWithBook:(RVBook *)book database:(Database *)database;
2789- (void) setPackage:(Package *)package;
2790
2791@end
2792
2793@implementation FileTable
2794
2795- (void) dealloc {
2796 if (package_ != nil)
2797 [package_ release];
2798 if (name_ != nil)
2799 [name_ release];
2800 [files_ release];
2801 [list_ release];
2802 [super dealloc];
2803}
2804
2805- (int) numberOfRowsInTable:(UITable *)table {
2806 return files_ == nil ? 0 : [files_ count];
2807}
2808
2809- (float) table:(UITable *)table heightForRow:(int)row {
2810 return 24;
2811}
2812
2813- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2814 if (reusing == nil) {
2815 reusing = [[[UIImageAndTextTableCell alloc] init] autorelease];
2816 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
2817 [[(UIImageAndTextTableCell *)reusing titleTextLabel] setFont:font];
2818 CFRelease(font);
2819 }
2820 [(UIImageAndTextTableCell *)reusing setTitle:[files_ objectAtIndex:row]];
2821 return reusing;
36bb2ca2 2822}
b19871dd 2823
58f6daef 2824- (BOOL) table:(UITable *)table canSelectRow:(int)row {
ab398adf
JF
2825 return NO;
2826}
b19871dd 2827
ab398adf
JF
2828- (id) initWithBook:(RVBook *)book database:(Database *)database {
2829 if ((self = [super initWithBook:book]) != nil) {
2830 database_ = database;
b19871dd 2831
ab398adf 2832 files_ = [[NSMutableArray arrayWithCapacity:32] retain];
8da60fb7 2833
ab398adf
JF
2834 list_ = [[UITable alloc] initWithFrame:[self bounds]];
2835 [self addSubview:list_];
2836
2837 UITableColumn *column = [[[UITableColumn alloc]
2838 initWithTitle:@"Name"
2839 identifier:@"name"
2840 width:[self frame].size.width
2841 ] autorelease];
2842
2843 [list_ setDataSource:self];
2844 [list_ setSeparatorStyle:1];
2845 [list_ addTableColumn:column];
2846 [list_ setDelegate:self];
2847 [list_ setReusesTableCells:YES];
2848 } return self;
2849}
2850
2851- (void) setPackage:(Package *)package {
2852 if (package_ != nil) {
2853 [package_ autorelease];
2854 package_ = nil;
2855 }
2856
2857 if (name_ != nil) {
2858 [name_ release];
2859 name_ = nil;
2860 }
2861
2862 [files_ removeAllObjects];
2863
2864 if (package != nil) {
2865 package_ = [package retain];
2866 name_ = [[package id] retain];
2867
9ea8d159
JF
2868 NSString *path = [NSString stringWithFormat:@"/var/lib/dpkg/info/%@.list", name_];
2869
2870 {
2871 std::ifstream fin([path UTF8String]);
2872 std::string line;
2873 while (std::getline(fin, line))
2874 [files_ addObject:[NSString stringWithUTF8String:line.c_str()]];
2875 }
ab398adf 2876
9ea8d159
JF
2877 if ([files_ count] != 0) {
2878 if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
2879 [files_ removeObjectAtIndex:0];
a54b1c10 2880 [files_ sortUsingSelector:@selector(compareByPath:)];
2388b078
JF
2881
2882 NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
2883 [stack addObject:@"/"];
2884
2885 for (int i(0), e([files_ count]); i != e; ++i) {
2886 NSString *file = [files_ objectAtIndex:i];
2887 while (![file hasPrefix:[stack lastObject]])
2888 [stack removeLastObject];
2889 NSString *directory = [stack lastObject];
2890 [stack addObject:[file stringByAppendingString:@"/"]];
2891 [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
a54b1c10 2892 ([stack count] - 2) * 3, "",
2388b078
JF
2893 [file substringFromIndex:[directory length]]
2894 ]];
2895 }
ab398adf
JF
2896 }
2897 }
2898
2899 [list_ reloadData];
2900}
2901
2902- (void) resetViewAnimated:(BOOL)animated {
2903 [list_ resetViewAnimated:animated];
2904}
2905
2906- (void) reloadData {
2907 [self setPackage:[database_ packageWithName:name_]];
2908 [self reloadButtons];
2909}
2910
2911- (NSString *) title {
2388b078
JF
2912 return @"Installed Files";
2913}
2914
2915- (NSString *) backButtonTitle {
2916 return @"Files";
ab398adf 2917}
b4d89997 2918
b4d89997 2919@end
36bb2ca2 2920/* }}} */
36bb2ca2
JF
2921/* Package View {{{ */
2922@protocol PackageViewDelegate
2923- (void) performPackage:(Package *)package;
2924@end
8da60fb7 2925
36bb2ca2
JF
2926@interface PackageView : RVPage {
2927 _transient Database *database_;
2928 UIPreferencesTable *table_;
2929 Package *package_;
2930 NSString *name_;
2931 UITextView *description_;
5a09ae08 2932 NSMutableArray *buttons_;
b31b87cc
JF
2933}
2934
36bb2ca2
JF
2935- (id) initWithBook:(RVBook *)book database:(Database *)database;
2936- (void) setPackage:(Package *)package;
b4d89997 2937
36bb2ca2 2938@end
b4d89997 2939
36bb2ca2 2940@implementation PackageView
b4d89997 2941
36bb2ca2
JF
2942- (void) dealloc {
2943 [table_ setDataSource:nil];
2944 [table_ setDelegate:nil];
b4d89997 2945
36bb2ca2
JF
2946 if (package_ != nil)
2947 [package_ release];
2948 if (name_ != nil)
2949 [name_ release];
2950 if (description_ != nil)
2951 [description_ release];
2952 [table_ release];
5a09ae08 2953 [buttons_ release];
36bb2ca2
JF
2954 [super dealloc];
2955}
8da60fb7 2956
36bb2ca2 2957- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
ab398adf
JF
2958 int number = 2;
2959 if ([package_ installed] != nil)
2960 ++number;
2961 if ([package_ source] != nil)
2962 ++number;
2963 return number;
36bb2ca2 2964}
b31b87cc 2965
36bb2ca2 2966- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
ab398adf
JF
2967 if (group-- == 0)
2968 return nil;
2969 else if ([package_ installed] != nil && group-- == 0)
2970 return @"Installed Package";
2971 else if (group-- == 0)
2972 return @"Package Details";
2973 else if ([package_ source] != nil && group-- == 0)
2974 return @"Source Information";
2975 else _assert(false);
36bb2ca2 2976}
8da60fb7 2977
36bb2ca2 2978- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
5a09ae08 2979 if (description_ == nil || group != 0 || row != 1)
36bb2ca2
JF
2980 return proposed;
2981 else
2982 return [description_ visibleTextRect].size.height + TextViewOffset_;
2983}
8da60fb7 2984
36bb2ca2 2985- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
ab398adf 2986 if (group-- == 0) {
5a09ae08 2987 int number = 1;
9bedffaa
JF
2988 if ([package_ author] != nil)
2989 ++number;
5a09ae08
JF
2990 if (description_ != nil)
2991 ++number;
ab398adf
JF
2992 if ([package_ website] != nil)
2993 ++number;
ab398adf
JF
2994 return number;
2995 } else if ([package_ installed] != nil && group-- == 0)
2996 return 2;
2388b078 2997 else if (group-- == 0) {
5a09ae08
JF
2998 int number = 2;
2999 if ([package_ size] != 0)
3000 ++number;
3001 if ([package_ maintainer] != nil)
3002 ++number;
795d26fc
JF
3003 if ([package_ sponsor] != nil)
3004 ++number;
2388b078
JF
3005 if ([package_ relationships] != nil)
3006 ++number;
31f3cfff
JF
3007 if ([[package_ source] trusted])
3008 ++number;
2388b078 3009 return number;
a54b1c10
JF
3010 } else if ([package_ source] != nil && group-- == 0) {
3011 Source *source = [package_ source];
3012 NSString *description = [source description];
5a09ae08 3013 int number = 1;
a54b1c10
JF
3014 if (description != nil && ![description isEqualToString:[source label]])
3015 ++number;
5a09ae08
JF
3016 if ([source origin] != nil)
3017 ++number;
a54b1c10
JF
3018 return number;
3019 } else _assert(false);
36bb2ca2 3020}
8da60fb7 3021
36bb2ca2
JF
3022- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
3023 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
3024 [cell setShowSelection:NO];
b4d89997 3025
2388b078 3026 if (group-- == 0) {
31f3cfff
JF
3027 if (false) {
3028 } else if (row-- == 0) {
2388b078
JF
3029 [cell setTitle:[package_ name]];
3030 [cell setValue:[package_ latest]];
9bedffaa
JF
3031 } else if ([package_ author] != nil && row-- == 0) {
3032 [cell setTitle:@"Author"];
3033 [cell setValue:[[package_ author] name]];
3034 [cell setShowDisclosure:YES];
3035 [cell setShowSelection:YES];
5a09ae08 3036 } else if (description_ != nil && row-- == 0) {
2388b078
JF
3037 [cell addSubview:description_];
3038 } else if ([package_ website] != nil && row-- == 0) {
3039 [cell setTitle:@"More Information"];
3040 [cell setShowDisclosure:YES];
3041 [cell setShowSelection:YES];
2388b078
JF
3042 } else _assert(false);
3043 } else if ([package_ installed] != nil && group-- == 0) {
31f3cfff
JF
3044 if (false) {
3045 } else if (row-- == 0) {
2388b078
JF
3046 [cell setTitle:@"Version"];
3047 NSString *installed([package_ installed]);
3048 [cell setValue:(installed == nil ? @"n/a" : installed)];
3049 } else if (row-- == 0) {
3050 [cell setTitle:@"Filesystem Content"];
3051 [cell setShowDisclosure:YES];
3052 [cell setShowSelection:YES];
3053 } else _assert(false);
3054 } else if (group-- == 0) {
31f3cfff
JF
3055 if (false) {
3056 } else if (row-- == 0) {
2388b078
JF
3057 [cell setTitle:@"Identifier"];
3058 [cell setValue:[package_ id]];
3059 } else if (row-- == 0) {
3060 [cell setTitle:@"Section"];
3061 NSString *section([package_ section]);
3062 [cell setValue:(section == nil ? @"n/a" : section)];
5a09ae08 3063 } else if ([package_ size] != 0 && row-- == 0) {
2388b078
JF
3064 [cell setTitle:@"Expanded Size"];
3065 [cell setValue:SizeString([package_ size])];
5a09ae08 3066 } else if ([package_ maintainer] != nil && row-- == 0) {
2388b078
JF
3067 [cell setTitle:@"Maintainer"];
3068 [cell setValue:[[package_ maintainer] name]];
3069 [cell setShowDisclosure:YES];
3070 [cell setShowSelection:YES];
795d26fc
JF
3071 } else if ([package_ sponsor] != nil && row-- == 0) {
3072 [cell setTitle:@"Sponsor"];
3073 [cell setValue:[[package_ sponsor] name]];
3074 [cell setShowDisclosure:YES];
3075 [cell setShowSelection:YES];
2388b078
JF
3076 } else if ([package_ relationships] != nil && row-- == 0) {
3077 [cell setTitle:@"Package Relationships"];
3078 [cell setShowDisclosure:YES];
3079 [cell setShowSelection:YES];
31f3cfff
JF
3080 } else if ([[package_ source] trusted] && row-- == 0) {
3081 [cell setIcon:[UIImage applicationImageNamed:@"trusted.png"]];
3082 [cell setValue:@"This package has been signed."];
2388b078
JF
3083 } else _assert(false);
3084 } else if ([package_ source] != nil && group-- == 0) {
a54b1c10
JF
3085 Source *source = [package_ source];
3086 NSString *description = [source description];
3087
31f3cfff
JF
3088 if (false) {
3089 } else if (row-- == 0) {
5a09ae08
JF
3090 NSString *label = [source label];
3091 if (label == nil)
3092 label = [source uri];
3093 [cell setTitle:label];
a54b1c10
JF
3094 [cell setValue:[source version]];
3095 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3096 [cell setValue:description];
5a09ae08 3097 } else if ([source origin] != nil && row-- == 0) {
2388b078 3098 [cell setTitle:@"Origin"];
a54b1c10 3099 [cell setValue:[source origin]];
2388b078
JF
3100 } else _assert(false);
3101 } else _assert(false);
dc5812ec 3102
36bb2ca2 3103 return cell;
b4d89997 3104}
dc5812ec 3105
36bb2ca2
JF
3106- (BOOL) canSelectRow:(int)row {
3107 return YES;
2367a917
JF
3108}
3109
36bb2ca2
JF
3110- (void) tableRowSelected:(NSNotification *)notification {
3111 int row = [table_ selectedRow];
31f3cfff
JF
3112 if (row == INT_MAX)
3113 return;
3114
3115 #define _else else goto _label; return; } _label:
3116
3117 if (true) {
3118 if (row-- == 0) {
3119 } else if (row-- == 0) {
3120 } else if ([package_ author] != nil && row-- == 0) {
3121 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3122 [[package_ author] email],
3123 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3124 [package_ name]
3125 ] stringByAddingPercentEscapes]
3126 ]]];
3127 } else if (description_ != nil && row-- == 0) {
3128 } else if ([package_ website] != nil && row-- == 0) {
3129 NSURL *url = [NSURL URLWithString:[package_ website]];
3130 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3131 [browser setDelegate:delegate_];
3132 [book_ pushPage:browser];
3133 [browser loadURL:url];
3134 } _else if ([package_ installed] != nil) {
3135 if (row-- == 0) {
3136 } else if (row-- == 0) {
3137 } else if (row-- == 0) {
3138 FileTable *files = [[[FileTable alloc] initWithBook:book_ database:database_] autorelease];
3139 [files setDelegate:delegate_];
3140 [files setPackage:package_];
3141 [book_ pushPage:files];
3142 } _else if (true) {
3143 if (row-- == 0) {
3144 } else if (row-- == 0) {
3145 } else if (row-- == 0) {
3146 } else if ([package_ size] != 0 && row-- == 0) {
3147 } else if ([package_ maintainer] != nil && row-- == 0) {
3148 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
3149 [[package_ maintainer] email],
3150 [[NSString stringWithFormat:@"regarding apt package \"%@\"",
3151 [package_ name]
3152 ] stringByAddingPercentEscapes]
3153 ]]];
795d26fc
JF
3154 } else if ([package_ sponsor] != nil && row-- == 0) {
3155 NSURL *url = [NSURL URLWithString:[[package_ sponsor] email]];
3156 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3157 [browser setDelegate:delegate_];
3158 [book_ pushPage:browser];
3159 [browser loadURL:url];
31f3cfff
JF
3160 } else if ([package_ relationships] != nil && row-- == 0) {
3161 } else if ([[package_ source] trusted] && row-- == 0) {
3162 } _else if ([package_ source] != nil) {
3163 Source *source = [package_ source];
3164 NSString *description = [source description];
3165
3166 if (row-- == 0) {
3167 } else if (row-- == 0) {
3168 } else if (description != nil && ![description isEqualToString:[source label]] && row-- == 0) {
3169 } else if ([source origin] != nil && row-- == 0) {
3170 } _else _assert(false);
795d26fc
JF
3171
3172 #undef _else
3178d79b
JF
3173}
3174
5a09ae08
JF
3175- (void) _clickButtonWithName:(NSString *)name {
3176 if ([name isEqualToString:@"Install"])
3177 [delegate_ installPackage:package_];
3178 else if ([name isEqualToString:@"Reinstall"])
3179 [delegate_ installPackage:package_];
3180 else if ([name isEqualToString:@"Remove"])
3181 [delegate_ removePackage:package_];
3182 else if ([name isEqualToString:@"Upgrade"])
3183 [delegate_ installPackage:package_];
3184 else _assert(false);
3185}
3186
36bb2ca2 3187- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
5a09ae08
JF
3188 int count = [buttons_ count];
3189 _assert(count != 0);
3190 _assert(button <= count + 1);
3191
3192 if (count != button - 1)
3193 [self _clickButtonWithName:[buttons_ objectAtIndex:(button - 1)]];
36bb2ca2
JF
3194
3195 [sheet dismiss];
b4d89997 3196}
2367a917 3197
36bb2ca2 3198- (void) _rightButtonClicked {
5a09ae08
JF
3199 int count = [buttons_ count];
3200 _assert(count != 0);
2367a917 3201
5a09ae08
JF
3202 if (count == 1)
3203 [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
3204 else {
3205 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:(count + 1)];
3206 [buttons addObjectsFromArray:buttons_];
36bb2ca2
JF
3207 [buttons addObject:@"Cancel"];
3208
3209 [delegate_ slideUp:[[[UIAlertSheet alloc]
9ea8d159 3210 initWithTitle:nil
36bb2ca2
JF
3211 buttons:buttons
3212 defaultButtonIndex:2
3213 delegate:self
a54b1c10 3214 context:@"manage"
36bb2ca2
JF
3215 ] autorelease]];
3216 }
b4d89997 3217}
2367a917 3218
36bb2ca2 3219- (NSString *) rightButtonTitle {
5a09ae08
JF
3220 int count = [buttons_ count];
3221 return count == 0 ? nil : count != 1 ? @"Modify" : [buttons_ objectAtIndex:0];
b4d89997 3222}
2367a917 3223
36bb2ca2
JF
3224- (NSString *) title {
3225 return @"Details";
dc5812ec
JF
3226}
3227
36bb2ca2
JF
3228- (id) initWithBook:(RVBook *)book database:(Database *)database {
3229 if ((self = [super initWithBook:book]) != nil) {
3230 database_ = database;
3231
3232 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
3233 [self addSubview:table_];
3234
3235 [table_ setDataSource:self];
3236 [table_ setDelegate:self];
5a09ae08
JF
3237
3238 buttons_ = [[NSMutableArray alloc] initWithCapacity:4];
36bb2ca2 3239 } return self;
dc5812ec
JF
3240}
3241
36bb2ca2
JF
3242- (void) setPackage:(Package *)package {
3243 if (package_ != nil) {
3244 [package_ autorelease];
3245 package_ = nil;
3246 }
3247
3248 if (name_ != nil) {
3249 [name_ release];
3250 name_ = nil;
3251 }
3252
3253 if (description_ != nil) {
3254 [description_ release];
3255 description_ = nil;
3256 }
3257
5a09ae08
JF
3258 [buttons_ removeAllObjects];
3259
36bb2ca2
JF
3260 if (package != nil) {
3261 package_ = [package retain];
3262 name_ = [[package id] retain];
3263
3264 NSString *description([package description]);
3265 if (description == nil)
3266 description = [package tagline];
5a09ae08
JF
3267 if (description != nil) {
3268 description_ = [GetTextView(description, 12, true) retain];
3269 [description_ setTextColor:Black_];
3270 }
36bb2ca2
JF
3271
3272 [table_ reloadData];
5a09ae08
JF
3273
3274 if ([package_ source] == nil);
31f3cfff
JF
3275 else if ([package_ upgradableAndEssential:NO])
3276 [buttons_ addObject:@"Upgrade"];
5a09ae08
JF
3277 else if ([package_ installed] == nil)
3278 [buttons_ addObject:@"Install"];
5a09ae08
JF
3279 else
3280 [buttons_ addObject:@"Reinstall"];
3281 if ([package_ installed] != nil)
3282 [buttons_ addObject:@"Remove"];
36bb2ca2 3283 }
dc5812ec
JF
3284}
3285
36bb2ca2
JF
3286- (void) resetViewAnimated:(BOOL)animated {
3287 [table_ resetViewAnimated:animated];
dc5812ec
JF
3288}
3289
36bb2ca2
JF
3290- (void) reloadData {
3291 [self setPackage:[database_ packageWithName:name_]];
3292 [self reloadButtons];
8fe19fc1
JF
3293}
3294
b4d89997
JF
3295@end
3296/* }}} */
b4d89997 3297/* Package Table {{{ */
36bb2ca2
JF
3298@interface PackageTable : RVPage {
3299 _transient Database *database_;
3300 NSString *title_;
3301 SEL filter_;
3302 id object_;
3303 NSMutableArray *packages_;
b4d89997 3304 NSMutableArray *sections_;
36bb2ca2 3305 UISectionList *list_;
dc5812ec
JF
3306}
3307
36bb2ca2 3308- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
b4d89997
JF
3309
3310- (void) setDelegate:(id)delegate;
a0be02eb
JF
3311- (void) setObject:(id)object;
3312
36bb2ca2 3313- (void) reloadData;
a0be02eb 3314- (void) resetCursor;
b4d89997 3315
2388b078
JF
3316- (UISectionList *) list;
3317
ab398adf
JF
3318- (void) setShouldHideHeaderInShortLists:(BOOL)hide;
3319
b4d89997
JF
3320@end
3321
3322@implementation PackageTable
3323
3324- (void) dealloc {
36bb2ca2
JF
3325 [list_ setDataSource:nil];
3326
3327 [title_ release];
3328 if (object_ != nil)
3329 [object_ release];
3330 [packages_ release];
b4d89997 3331 [sections_ release];
36bb2ca2 3332 [list_ release];
b4d89997 3333 [super dealloc];
8fe19fc1
JF
3334}
3335
b4d89997
JF
3336- (int) numberOfSectionsInSectionList:(UISectionList *)list {
3337 return [sections_ count];
3338}
2367a917 3339
b4d89997
JF
3340- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3341 return [[sections_ objectAtIndex:section] name];
3342}
dc5812ec 3343
b4d89997
JF
3344- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3345 return [[sections_ objectAtIndex:section] row];
2367a917
JF
3346}
3347
b4d89997
JF
3348- (int) numberOfRowsInTable:(UITable *)table {
3349 return [packages_ count];
3350}
dc5812ec 3351
b4d89997 3352- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 3353 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
b4d89997 3354}
dc5812ec 3355
b4d89997
JF
3356- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3357 if (reusing == nil)
36bb2ca2 3358 reusing = [[[PackageCell alloc] init] autorelease];
b4d89997
JF
3359 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3360 return reusing;
3361}
dc5812ec 3362
b4d89997
JF
3363- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3364 return NO;
3365}
dc5812ec 3366
b4d89997
JF
3367- (void) tableRowSelected:(NSNotification *)notification {
3368 int row = [[notification object] selectedRow];
36bb2ca2
JF
3369 if (row == INT_MAX)
3370 return;
3371
3372 Package *package = [packages_ objectAtIndex:row];
3373 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3374 [view setDelegate:delegate_];
3375 [view setPackage:package];
3376 [book_ pushPage:view];
dc5812ec
JF
3377}
3378
36bb2ca2
JF
3379- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
3380 if ((self = [super initWithBook:book]) != nil) {
3381 database_ = database;
3382 title_ = [title retain];
3383 filter_ = filter;
3384 object_ = object == nil ? nil : [object retain];
3385
3386 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
b4d89997 3387 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 3388
b4d89997
JF
3389 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
3390 [list_ setDataSource:self];
dc5812ec 3391
b4d89997
JF
3392 UITableColumn *column = [[[UITableColumn alloc]
3393 initWithTitle:@"Name"
3394 identifier:@"name"
36bb2ca2 3395 width:[self frame].size.width
b4d89997
JF
3396 ] autorelease];
3397
3398 UITable *table = [list_ table];
3399 [table setSeparatorStyle:1];
3400 [table addTableColumn:column];
3401 [table setDelegate:self];
3402 [table setReusesTableCells:YES];
3403
3404 [self addSubview:list_];
36bb2ca2 3405 [self reloadData];
b4d89997 3406 } return self;
dc5812ec
JF
3407}
3408
3409- (void) setDelegate:(id)delegate {
2367a917 3410 delegate_ = delegate;
b4d89997
JF
3411}
3412
a0be02eb
JF
3413- (void) setObject:(id)object {
3414 if (object_ != nil)
3415 [object_ release];
3416 if (object == nil)
3417 object_ = nil;
3418 else
3419 object_ = [object retain];
3420}
3421
36bb2ca2
JF
3422- (void) reloadData {
3423 NSArray *packages = [database_ packages];
b4d89997 3424
36bb2ca2 3425 [packages_ removeAllObjects];
b4d89997
JF
3426 [sections_ removeAllObjects];
3427
36bb2ca2
JF
3428 for (size_t i(0); i != [packages count]; ++i) {
3429 Package *package([packages objectAtIndex:i]);
3430 if ([[package performSelector:filter_ withObject:object_] boolValue])
3431 [packages_ addObject:package];
3432 }
3433
b4d89997
JF
3434 Section *section = nil;
3435
3436 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3437 Package *package = [packages_ objectAtIndex:offset];
3438 NSString *name = [package index];
3439
36bb2ca2 3440 if (section == nil || ![[section name] isEqualToString:name]) {
b4d89997
JF
3441 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3442 [sections_ addObject:section];
3443 }
3444
36bb2ca2 3445 [section addToCount];
b4d89997
JF
3446 }
3447
3448 [list_ reloadData];
3449}
3450
36bb2ca2
JF
3451- (NSString *) title {
3452 return title_;
b4d89997
JF
3453}
3454
36bb2ca2
JF
3455- (void) resetViewAnimated:(BOOL)animated {
3456 [list_ resetViewAnimated:animated];
dc5812ec
JF
3457}
3458
a0be02eb
JF
3459- (void) resetCursor {
3460 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3461}
3462
2388b078
JF
3463- (UISectionList *) list {
3464 return list_;
3465}
3466
ab398adf
JF
3467- (void) setShouldHideHeaderInShortLists:(BOOL)hide {
3468 [list_ setShouldHideHeaderInShortLists:hide];
3469}
3470
dc5812ec 3471@end
b4d89997 3472/* }}} */
dc5812ec 3473
36bb2ca2
JF
3474/* Browser Implementation {{{ */
3475@implementation BrowserView
3476
3477- (void) dealloc {
3478 WebView *webview = [webview_ webView];
3479 [webview setFrameLoadDelegate:nil];
3480 [webview setResourceLoadDelegate:nil];
3481 [webview setUIDelegate:nil];
3482
3483 [scroller_ setDelegate:nil];
3484 [webview_ setDelegate:nil];
3485
3486 [scroller_ release];
3487 [webview_ release];
3488 [urls_ release];
3489 [indicator_ release];
3490 if (title_ != nil)
3491 [title_ release];
3492 [super dealloc];
dc5812ec
JF
3493}
3494
36bb2ca2
JF
3495- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3496 NSMutableURLRequest *request = [NSMutableURLRequest
3497 requestWithURL:url
3498 cachePolicy:policy
3499 timeoutInterval:30.0
3500 ];
b4d89997 3501
2388b078
JF
3502 [request addValue:[NSString stringWithUTF8String:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3503 [request addValue:[NSString stringWithUTF8String:Machine_] forHTTPHeaderField:@"X-Machine"];
3504 [request addValue:[NSString stringWithUTF8String:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
b4d89997 3505
36bb2ca2
JF
3506 [self loadRequest:request];
3507}
dc5812ec 3508
dc5812ec 3509
36bb2ca2
JF
3510- (void) loadURL:(NSURL *)url {
3511 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3512}
dc5812ec 3513
36bb2ca2
JF
3514// XXX: this needs to add the headers
3515- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3516 return request;
b4d89997
JF
3517}
3518
36bb2ca2
JF
3519- (void) loadRequest:(NSURLRequest *)request {
3520 [webview_ loadRequest:request];
3521}
b4d89997 3522
36bb2ca2
JF
3523- (void) reloadURL {
3524 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3525 [urls_ removeLastObject];
3526 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3527}
b4d89997 3528
36bb2ca2
JF
3529- (WebView *) webView {
3530 return [webview_ webView];
3531}
b4d89997 3532
36bb2ca2
JF
3533- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3534 [scroller_ setContentSize:frame.size];
3535}
b4d89997 3536
36bb2ca2
JF
3537- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3538 [self view:sender didSetFrame:frame];
3539}
b4d89997 3540
36bb2ca2
JF
3541- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3542 return [self _addHeadersToRequest:request];
3543}
b4d89997 3544
36bb2ca2 3545- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
a07b2a71
JF
3546 if ([[[request URL] scheme] isEqualToString:@"apptapp"])
3547 return nil;
36bb2ca2
JF
3548 [self setBackButtonTitle:title_];
3549 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3550 [browser setDelegate:delegate_];
3551 [book_ pushPage:browser];
3552 [browser loadRequest:[self _addHeadersToRequest:request]];
3553 return [browser webView];
3554}
3555
3556- (void) webView:(WebView *)sender willClickElement:(id)element {
3557 if (![element respondsToSelector:@selector(href)])
3558 return;
3559 NSString *href = [element href];
3560 if (href == nil)
3561 return;
3562 if ([href hasPrefix:@"apptapp://package/"]) {
3563 NSString *name = [href substringFromIndex:18];
3564 Package *package = [database_ packageWithName:name];
3565 if (package == nil) {
3566 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3567 initWithTitle:@"Cannot Locate Package"
3568 buttons:[NSArray arrayWithObjects:@"Close", nil]
3569 defaultButtonIndex:0
3570 delegate:self
a54b1c10 3571 context:@"missing"
36bb2ca2 3572 ] autorelease];
b4d89997 3573
36bb2ca2 3574 [sheet setBodyText:[NSString stringWithFormat:
d36e83a3 3575 @"The package %@ cannot be found in your current sources. I might recommend installing more sources."
36bb2ca2 3576 , name]];
b4d89997 3577
36bb2ca2
JF
3578 [sheet popupAlertAnimated:YES];
3579 } else {
3580 [self setBackButtonTitle:title_];
3581 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3582 [view setDelegate:delegate_];
3583 [view setPackage:package];
3584 [book_ pushPage:view];
3585 }
3586 }
dc5812ec
JF
3587}
3588
36bb2ca2
JF
3589- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3590 title_ = [title retain];
3591 [self setTitle:title];
3592}
3593
3594- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3595 if ([frame parentFrame] != nil)
3596 return;
3597
1d3b229d 3598 reloading_ = false;
36bb2ca2
JF
3599 loading_ = true;
3600 [indicator_ startAnimation];
3601 [self reloadButtons];
3602
3603 if (title_ != nil) {
3604 [title_ release];
3605 title_ = nil;
b4d89997 3606 }
36bb2ca2
JF
3607
3608 [self setTitle:@"Loading..."];
3609
3610 WebView *webview = [webview_ webView];
3611 NSString *href = [webview mainFrameURL];
3612 [urls_ addObject:[NSURL URLWithString:href]];
3613
3614 CGRect webrect = [scroller_ frame];
3615 webrect.size.height = 0;
3616 [webview_ setFrame:webrect];
dc5812ec
JF
3617}
3618
36bb2ca2 3619- (void) _finishLoading {
1d3b229d
JF
3620 if (!reloading_) {
3621 loading_ = false;
3622 [indicator_ stopAnimation];
3623 [self reloadButtons];
3624 }
36bb2ca2 3625}
b4d89997 3626
36bb2ca2
JF
3627- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3628 if ([frame parentFrame] != nil)
3629 return;
3630 [self _finishLoading];
3631}
b4d89997 3632
36bb2ca2
JF
3633- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3634 if ([frame parentFrame] != nil)
3635 return;
3636 [self setTitle:[error localizedDescription]];
3637 [self _finishLoading];
3638}
3639
3640- (id) initWithBook:(RVBook *)book database:(Database *)database {
3641 if ((self = [super initWithBook:book]) != nil) {
3642 database_ = database;
3643 loading_ = false;
3644
3645 struct CGRect bounds = [self bounds];
3646
3647 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3648 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3649 [self addSubview:pinstripe];
3650
3651 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3652 [self addSubview:scroller_];
3653
3654 [scroller_ setScrollingEnabled:YES];
3655 [scroller_ setAdjustForContentSizeChange:YES];
3656 [scroller_ setClipsSubviews:YES];
3657 [scroller_ setAllowsRubberBanding:YES];
3658 [scroller_ setScrollDecelerationFactor:0.99];
3659 [scroller_ setDelegate:self];
3660
3661 CGRect webrect = [scroller_ bounds];
3662 webrect.size.height = 0;
3663
3664 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3665 [scroller_ addSubview:webview_];
3666
3667 [webview_ setTilingEnabled:YES];
3668 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3669 [webview_ setAutoresizes:YES];
3670 [webview_ setDelegate:self];
3671 //[webview_ setEnabledGestures:2];
b4d89997 3672
795d26fc 3673 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:kUIProgressIndicatorStyleMediumWhite];
a54b1c10 3674 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 42, indsize.width, indsize.height)];
795d26fc 3675 [indicator_ setStyle:kUIProgressIndicatorStyleMediumWhite];
36bb2ca2
JF
3676
3677 Package *package([database_ packageWithName:@"cydia"]);
3678 NSString *application = package == nil ? @"Cydia" : [NSString
3679 stringWithFormat:@"Cydia/%@",
3680 [package installed]
3681 ];
3682
3683 WebView *webview = [webview_ webView];
3684 [webview setApplicationNameForUserAgent:application];
3685 [webview setFrameLoadDelegate:self];
3686 [webview setResourceLoadDelegate:self];
3687 [webview setUIDelegate:self];
3688
3689 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3690 } return self;
dc5812ec
JF
3691}
3692
36bb2ca2
JF
3693- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3694 [sheet dismiss];
3695}
3696
3697- (void) _leftButtonClicked {
3698 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
199d0ba5 3699 initWithTitle:@"About Cydia Installer"
36bb2ca2
JF
3700 buttons:[NSArray arrayWithObjects:@"Close", nil]
3701 defaultButtonIndex:0
3702 delegate:self
a54b1c10 3703 context:@"about"
36bb2ca2
JF
3704 ] autorelease];
3705
3706 [sheet setBodyText:
3707 @"Copyright (C) 2008\n"
3708 "Jay Freeman (saurik)\n"
3709 "saurik@saurik.com\n"
3710 "http://www.saurik.com/\n"
3711 "\n"
3712 "The Okori Group\n"
3713 "http://www.theokorigroup.com/\n"
3714 "\n"
3715 "College of Creative Studies,\n"
3716 "University of California,\n"
3717 "Santa Barbara\n"
3718 "http://www.ccs.ucsb.edu/"
3719 ];
3720
3721 [sheet popupAlertAnimated:YES];
3722}
3723
3724- (void) _rightButtonClicked {
1d3b229d 3725 reloading_ = true;
36bb2ca2
JF
3726 [self reloadURL];
3727}
3728
3729- (NSString *) leftButtonTitle {
3730 return @"About";
3731}
3732
3733- (NSString *) rightButtonTitle {
3734 return loading_ ? @"" : @"Reload";
3735}
3736
3737- (NSString *) title {
3738 return nil;
3739}
3740
3741- (NSString *) backButtonTitle {
3742 return @"Browser";
dc5812ec
JF
3743}
3744
36bb2ca2
JF
3745- (void) setPageActive:(BOOL)active {
3746 if (active)
3747 [book_ addSubview:indicator_];
3748 else
3749 [indicator_ removeFromSuperview];
dc5812ec
JF
3750}
3751
36bb2ca2 3752- (void) resetViewAnimated:(BOOL)animated {
b4d89997 3753}
2367a917 3754
b4d89997
JF
3755@end
3756/* }}} */
36bb2ca2 3757
9a7b04c5
JF
3758@interface CYBook : RVBook <
3759 ProgressDelegate
3760> {
3761 _transient Database *database_;
3762 UIView *overlay_;
3763 UIProgressIndicator *indicator_;
3764 UITextLabel *prompt_;
3765 UIProgressBar *progress_;
3766 bool updating_;
3767}
3768
3769- (id) initWithFrame:(CGRect)frame database:(Database *)database;
3770- (void) update;
3771- (BOOL) updating;
3772
3773@end
3774
b4d89997 3775/* Install View {{{ */
36bb2ca2
JF
3776@interface InstallView : RVPage {
3777 _transient Database *database_;
b4d89997 3778 NSMutableArray *packages_;
36bb2ca2
JF
3779 NSMutableArray *sections_;
3780 UITable *list_;
a54b1c10 3781 UIView *accessory_;
b4d89997 3782}
dc5812ec 3783
36bb2ca2
JF
3784- (id) initWithBook:(RVBook *)book database:(Database *)database;
3785- (void) reloadData;
dc5812ec 3786
2367a917
JF
3787@end
3788
b4d89997 3789@implementation InstallView
dc5812ec 3790
2367a917 3791- (void) dealloc {
36bb2ca2
JF
3792 [list_ setDataSource:nil];
3793 [list_ setDelegate:nil];
3794
b4d89997 3795 [packages_ release];
36bb2ca2
JF
3796 [sections_ release];
3797 [list_ release];
a54b1c10 3798 [accessory_ release];
2367a917
JF
3799 [super dealloc];
3800}
3801
b4d89997
JF
3802- (int) numberOfRowsInTable:(UITable *)table {
3803 return [sections_ count] + 1;
3804}
8da60fb7 3805
b4d89997
JF
3806- (float) table:(UITable *)table heightForRow:(int)row {
3807 return 45;
3808}
8da60fb7 3809
b4d89997
JF
3810- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3811 if (reusing == nil)
3812 reusing = [[[SectionCell alloc] init] autorelease];
36bb2ca2 3813 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
b4d89997
JF
3814 return reusing;
3815}
dc5812ec 3816
b4d89997
JF
3817- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3818 return YES;
3819}
dc5812ec 3820
b4d89997
JF
3821- (void) tableRowSelected:(NSNotification *)notification {
3822 int row = [[notification object] selectedRow];
36bb2ca2
JF
3823 if (row == INT_MAX)
3824 return;
dc5812ec 3825
36bb2ca2 3826 Section *section;
5a09ae08 3827 NSString *name;
36bb2ca2 3828 NSString *title;
dc5812ec 3829
36bb2ca2
JF
3830 if (row == 0) {
3831 section = nil;
5a09ae08 3832 name = nil;
36bb2ca2 3833 title = @"All Packages";
b4d89997 3834 } else {
36bb2ca2 3835 section = [sections_ objectAtIndex:(row - 1)];
5a09ae08
JF
3836 name = [section name];
3837
3838 if (name != nil)
3839 title = name;
3840 else {
3841 name = @"";
3842 title = @"(No Section)";
3843 }
b4d89997 3844 }
2367a917 3845
36bb2ca2
JF
3846 PackageTable *table = [[[PackageTable alloc]
3847 initWithBook:book_
3848 database:database_
3849 title:title
3850 filter:@selector(isUninstalledInSection:)
5a09ae08 3851 with:name
36bb2ca2 3852 ] autorelease];
2367a917 3853
36bb2ca2 3854 [table setDelegate:delegate_];
2367a917 3855
36bb2ca2 3856 [book_ pushPage:table];
b4d89997 3857}
2367a917 3858
36bb2ca2
JF
3859- (id) initWithBook:(RVBook *)book database:(Database *)database {
3860 if ((self = [super initWithBook:book]) != nil) {
3861 database_ = database;
3862
b4d89997 3863 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
36bb2ca2 3864 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2367a917 3865
36bb2ca2
JF
3866 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3867 [self addSubview:list_];
dc5812ec 3868
b4d89997
JF
3869 UITableColumn *column = [[[UITableColumn alloc]
3870 initWithTitle:@"Name"
3871 identifier:@"name"
36bb2ca2 3872 width:[self frame].size.width
b4d89997 3873 ] autorelease];
dc5812ec 3874
b4d89997
JF
3875 [list_ setDataSource:self];
3876 [list_ setSeparatorStyle:1];
3877 [list_ addTableColumn:column];
3878 [list_ setDelegate:self];
3879 [list_ setReusesTableCells:YES];
2367a917 3880
36bb2ca2 3881 [self reloadData];
b4d89997 3882 } return self;
2367a917
JF
3883}
3884
36bb2ca2
JF
3885- (void) reloadData {
3886 NSArray *packages = [database_ packages];
3887
b4d89997 3888 [packages_ removeAllObjects];
36bb2ca2 3889 [sections_ removeAllObjects];
dc5812ec 3890
b4d89997
JF
3891 for (size_t i(0); i != [packages count]; ++i) {
3892 Package *package([packages objectAtIndex:i]);
5a09ae08 3893 if ([package valid] && [package installed] == nil)
b4d89997
JF
3894 [packages_ addObject:package];
3895 }
dc5812ec 3896
36bb2ca2 3897 [packages_ sortUsingSelector:@selector(compareBySection:)];
dc5812ec 3898
b4d89997
JF
3899 Section *section = nil;
3900 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3901 Package *package = [packages_ objectAtIndex:offset];
3902 NSString *name = [package section];
2367a917 3903
36bb2ca2 3904 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
b4d89997 3905 section = [[[Section alloc] initWithName:name row:offset] autorelease];
36bb2ca2 3906 [sections_ addObject:section];
b4d89997 3907 }
dc5812ec 3908
36bb2ca2 3909 [section addToCount];
b4d89997 3910 }
2367a917 3911
b4d89997 3912 [list_ reloadData];
36bb2ca2 3913}
2367a917 3914
36bb2ca2
JF
3915- (void) resetViewAnimated:(BOOL)animated {
3916 [list_ resetViewAnimated:animated];
3917}
2367a917 3918
36bb2ca2
JF
3919- (NSString *) title {
3920 return @"Install";
2367a917
JF
3921}
3922
36bb2ca2
JF
3923- (NSString *) backButtonTitle {
3924 return @"Sections";
dc5812ec
JF
3925}
3926
a54b1c10
JF
3927- (UIView *) accessoryView {
3928 return accessory_;
3929}
3930
dc5812ec 3931@end
2367a917 3932/* }}} */
b4d89997 3933/* Changes View {{{ */
36bb2ca2
JF
3934@interface ChangesView : RVPage {
3935 _transient Database *database_;
dc5812ec
JF
3936 NSMutableArray *packages_;
3937 NSMutableArray *sections_;
36bb2ca2
JF
3938 UISectionList *list_;
3939 unsigned upgrades_;
dc5812ec
JF
3940}
3941
36bb2ca2
JF
3942- (id) initWithBook:(RVBook *)book database:(Database *)database;
3943- (void) reloadData;
dc5812ec 3944
dc5812ec
JF
3945@end
3946
b4d89997
JF
3947@implementation ChangesView
3948
3949- (void) dealloc {
36bb2ca2
JF
3950 [[list_ table] setDelegate:nil];
3951 [list_ setDataSource:nil];
3952
b4d89997
JF
3953 [packages_ release];
3954 [sections_ release];
36bb2ca2 3955 [list_ release];
b4d89997
JF
3956 [super dealloc];
3957}
dc5812ec
JF
3958
3959- (int) numberOfSectionsInSectionList:(UISectionList *)list {
3960 return [sections_ count];
3961}
3962
3963- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3964 return [[sections_ objectAtIndex:section] name];
3965}
3966
3967- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3968 return [[sections_ objectAtIndex:section] row];
3969}
3970
3971- (int) numberOfRowsInTable:(UITable *)table {
3972 return [packages_ count];
3973}
3974
3975- (float) table:(UITable *)table heightForRow:(int)row {
9bedffaa 3976 return [PackageCell heightForPackage:[packages_ objectAtIndex:row]];
dc5812ec
JF
3977}
3978
1d80f6b9
JF
3979- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3980 if (reusing == nil)
36bb2ca2 3981 reusing = [[[PackageCell alloc] init] autorelease];
1d80f6b9
JF
3982 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3983 return reusing;
dc5812ec
JF
3984}
3985
3986- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
b4d89997 3987 return NO;
dc5812ec
JF
3988}
3989
b4d89997
JF
3990- (void) tableRowSelected:(NSNotification *)notification {
3991 int row = [[notification object] selectedRow];
36bb2ca2
JF
3992 if (row == INT_MAX)
3993 return;
3994 Package *package = [packages_ objectAtIndex:row];
3995 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3996 [view setDelegate:delegate_];
3997 [view setPackage:package];
3998 [book_ pushPage:view];
dc5812ec
JF
3999}
4000
9a7b04c5
JF
4001- (void) _leftButtonClicked {
4002 [(CYBook *)book_ update];
4003 [self reloadButtons];
4004}
4005
36bb2ca2
JF
4006- (void) _rightButtonClicked {
4007 [delegate_ distUpgrade];
dc5812ec
JF
4008}
4009
36bb2ca2
JF
4010- (id) initWithBook:(RVBook *)book database:(Database *)database {
4011 if ((self = [super initWithBook:book]) != nil) {
4012 database_ = database;
dc5812ec 4013
b4d89997
JF
4014 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
4015 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 4016
36bb2ca2
JF
4017 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
4018 [self addSubview:list_];
4019
3178d79b 4020 [list_ setShouldHideHeaderInShortLists:NO];
b4d89997
JF
4021 [list_ setDataSource:self];
4022 //[list_ setSectionListStyle:1];
dc5812ec 4023
b4d89997 4024 UITableColumn *column = [[[UITableColumn alloc]
dc5812ec
JF
4025 initWithTitle:@"Name"
4026 identifier:@"name"
36bb2ca2 4027 width:[self frame].size.width
b4d89997 4028 ] autorelease];
dc5812ec
JF
4029
4030 UITable *table = [list_ table];
4031 [table setSeparatorStyle:1];
4032 [table addTableColumn:column];
4033 [table setDelegate:self];
1d80f6b9 4034 [table setReusesTableCells:YES];
dc5812ec 4035
36bb2ca2 4036 [self reloadData];
dc5812ec
JF
4037 } return self;
4038}
4039
36bb2ca2
JF
4040- (void) reloadData {
4041 NSArray *packages = [database_ packages];
4042
b4d89997 4043 [packages_ removeAllObjects];
36bb2ca2
JF
4044 [sections_ removeAllObjects];
4045
b4d89997
JF
4046 for (size_t i(0); i != [packages count]; ++i) {
4047 Package *package([packages objectAtIndex:i]);
31f3cfff 4048 if ([package installed] == nil && [package valid] || [package upgradableAndEssential:NO])
b4d89997
JF
4049 [packages_ addObject:package];
4050 }
2367a917 4051
b4d89997 4052 [packages_ sortUsingSelector:@selector(compareForChanges:)];
dc5812ec 4053
b4d89997 4054 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
dc5812ec 4055 Section *section = nil;
b4d89997 4056
36bb2ca2
JF
4057 upgrades_ = 0;
4058 bool unseens = false;
4059
199d0ba5 4060 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
36bb2ca2 4061
dc5812ec
JF
4062 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
4063 Package *package = [packages_ objectAtIndex:offset];
dc5812ec 4064
31f3cfff 4065 if ([package upgradableAndEssential:YES]) {
36bb2ca2
JF
4066 ++upgrades_;
4067 [upgradable addToCount];
4068 } else {
4069 unseens = true;
b4d89997 4070 NSDate *seen = [package seen];
dc5812ec 4071
723a0072 4072 NSString *name;
723a0072
JF
4073
4074 if (seen == nil)
36bb2ca2 4075 name = [@"n/a ?" retain];
723a0072 4076 else {
36bb2ca2 4077 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
723a0072 4078 }
b4d89997 4079
36bb2ca2 4080 if (section == nil || ![[section name] isEqualToString:name]) {
b4d89997
JF
4081 section = [[[Section alloc] initWithName:name row:offset] autorelease];
4082 [sections_ addObject:section];
4083 }
4084
36bb2ca2
JF
4085 [name release];
4086 [section addToCount];
b4d89997 4087 }
dc5812ec
JF
4088 }
4089
36bb2ca2 4090 CFRelease(formatter);
b4d89997 4091
36bb2ca2
JF
4092 if (unseens) {
4093 Section *last = [sections_ lastObject];
4094 size_t count = [last count];
4095 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
4096 [sections_ removeLastObject];
4097 }
dc5812ec 4098
36bb2ca2
JF
4099 if (upgrades_ != 0)
4100 [sections_ insertObject:upgradable atIndex:0];
c25a610d 4101
36bb2ca2
JF
4102 [list_ reloadData];
4103 [self reloadButtons];
3178d79b
JF
4104}
4105
36bb2ca2
JF
4106- (void) resetViewAnimated:(BOOL)animated {
4107 [list_ resetViewAnimated:animated];
3178d79b
JF
4108}
4109
9a7b04c5
JF
4110- (NSString *) leftButtonTitle {
4111 return [(CYBook *)book_ updating] ? nil : @"Refresh";
4112}
4113
36bb2ca2
JF
4114- (NSString *) rightButtonTitle {
4115 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3178d79b
JF
4116}
4117
36bb2ca2
JF
4118- (NSString *) title {
4119 return @"Changes";
8fe19fc1
JF
4120}
4121
559dec34
JF
4122@end
4123/* }}} */
4124/* Manage View {{{ */
4125@interface ManageView : PackageTable {
4126}
4127
4128- (id) initWithBook:(RVBook *)book database:(Database *)database;
4129
4130@end
4131
4132@implementation ManageView
4133
4134- (id) initWithBook:(RVBook *)book database:(Database *)database {
4135 if ((self = [super
4136 initWithBook:book
4137 database:database
4138 title:nil
4139 filter:@selector(isInstalledInSection:)
4140 with:nil
4141 ]) != nil) {
4142 } return self;
4143}
4144
4145- (NSString *) title {
4146 return @"Installed Packages";
4147}
4148
4149- (NSString *) backButtonTitle {
4150 return @"All Packages";
4151}
4152
3178d79b
JF
4153@end
4154/* }}} */
36bb2ca2
JF
4155/* Search View {{{ */
4156@protocol SearchViewDelegate
4157- (void) showKeyboard:(BOOL)show;
4158@end
b4d89997 4159
a0be02eb 4160@interface SearchView : RVPage {
36bb2ca2
JF
4161 UIView *accessory_;
4162 UISearchField *field_;
a0be02eb
JF
4163 UITransitionView *transition_;
4164 PackageTable *table_;
4165 UIPreferencesTable *advanced_;
9b619239 4166 UIView *dimmed_;
a0be02eb 4167 bool flipped_;
ab398adf 4168 bool reload_;
36bb2ca2 4169}
b4d89997 4170
36bb2ca2
JF
4171- (id) initWithBook:(RVBook *)book database:(Database *)database;
4172- (void) reloadData;
b4d89997 4173
3178d79b
JF
4174@end
4175
36bb2ca2 4176@implementation SearchView
3178d79b 4177
b4d89997 4178- (void) dealloc {
36bb2ca2
JF
4179#ifndef __OBJC2__
4180 [[field_ textTraits] setEditingDelegate:nil];
4181#endif
4182 [field_ setDelegate:nil];
3178d79b 4183
36bb2ca2
JF
4184 [accessory_ release];
4185 [field_ release];
a0be02eb
JF
4186 [transition_ release];
4187 [table_ release];
4188 [advanced_ release];
9b619239 4189 [dimmed_ release];
36bb2ca2 4190 [super dealloc];
b4d89997
JF
4191}
4192
ab398adf
JF
4193- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
4194 return 1;
4195}
4196
4197- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
4198 switch (group) {
4199 case 0: return @"Advanced Search (Coming Soon!)";
4200
4201 default: _assert(false);
4202 }
4203}
4204
4205- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
4206 switch (group) {
4207 case 0: return 0;
4208
4209 default: _assert(false);
4210 }
4211}
4212
2388b078
JF
4213- (void) _showKeyboard:(BOOL)show {
4214 CGSize keysize = [UIKeyboard defaultSize];
4215 CGRect keydown = [book_ pageBounds];
4216 CGRect keyup = keydown;
4217 keyup.size.height -= keysize.height - ButtonBarHeight_;
4218
4219 float delay = KeyboardTime_ * ButtonBarHeight_ / keysize.height;
4220
4221 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:[table_ list]] autorelease];
4222 [animation setSignificantRectFields:8];
4223
4224 if (show) {
4225 [animation setStartFrame:keydown];
4226 [animation setEndFrame:keyup];
4227 } else {
4228 [animation setStartFrame:keyup];
4229 [animation setEndFrame:keydown];
4230 }
4231
4232 UIAnimator *animator = [UIAnimator sharedAnimator];
4233
4234 [animator
4235 addAnimations:[NSArray arrayWithObjects:animation, nil]
4236 withDuration:(KeyboardTime_ - delay)
4237 start:!show
4238 ];
4239
4240 if (show)
4241 [animator performSelector:@selector(startAnimation:) withObject:animation afterDelay:delay];
4242
8cc774c5 4243#ifndef __OBJC2__
2388b078 4244 [delegate_ showKeyboard:show];
8cc774c5 4245#endif
2388b078
JF
4246}
4247
36bb2ca2 4248- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
2388b078 4249 [self _showKeyboard:YES];
36bb2ca2 4250}
b4d89997 4251
36bb2ca2 4252- (void) textFieldDidResignFirstResponder:(UITextField *)field {
2388b078 4253 [self _showKeyboard:NO];
36bb2ca2 4254}
b4d89997 4255
36bb2ca2 4256- (void) keyboardInputChanged:(UIFieldEditor *)editor {
ab398adf
JF
4257 if (reload_) {
4258 NSString *text([field_ text]);
4259 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
4260 [self reloadData];
4261 reload_ = false;
4262 }
4263}
4264
4265- (void) textFieldClearButtonPressed:(UITextField *)field {
4266 reload_ = true;
4267}
4268
4269- (void) keyboardInputShouldDelete:(id)input {
4270 reload_ = true;
36bb2ca2 4271}
b4d89997 4272
36bb2ca2 4273- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
ab398adf
JF
4274 if ([text length] != 1 || [text characterAtIndex:0] != '\n') {
4275 reload_ = true;
36bb2ca2 4276 return YES;
ab398adf
JF
4277 } else {
4278 [field_ resignFirstResponder];
4279 return NO;
4280 }
b4d89997
JF
4281}
4282
36bb2ca2 4283- (id) initWithBook:(RVBook *)book database:(Database *)database {
a0be02eb
JF
4284 if ((self = [super initWithBook:book]) != nil) {
4285 CGRect pageBounds = [book_ pageBounds];
4286
7fea16f2 4287 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
a0be02eb 4288 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
7fea16f2 4289 [self addSubview:pinstripe];*/
a0be02eb 4290
a0be02eb
JF
4291 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
4292 [self addSubview:transition_];
4293
4294 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
4295
ab398adf
JF
4296 [advanced_ setReusesTableCells:YES];
4297 [advanced_ setDataSource:self];
4298 [advanced_ reloadData];
4299
9b619239
JF
4300 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
4301 CGColor dimmed(space_, 0, 0, 0, 0.5);
4302 [dimmed_ setBackgroundColor:dimmed];
4303
a0be02eb
JF
4304 table_ = [[PackageTable alloc]
4305 initWithBook:book
4306 database:database
4307 title:nil
4308 filter:@selector(isSearchedForBy:)
4309 with:nil
4310 ];
4311
ab398adf 4312 [table_ setShouldHideHeaderInShortLists:NO];
a0be02eb
JF
4313 [transition_ transition:0 toView:table_];
4314
8cc774c5
JF
4315 CGRect cnfrect = {{
4316#ifdef __OBJC2__
4317 6 +
4318#endif
4319 1, 38}, {17, 18}};
b4d89997 4320
36bb2ca2 4321 CGRect area;
8cc774c5 4322 area.origin.x = cnfrect.origin.x + cnfrect.size.width + 14;
36bb2ca2 4323 area.origin.y = 30;
8cc774c5
JF
4324
4325 area.size.width =
4326#ifdef __OBJC2__
4327 8 +
4328#endif
4329 [self bounds].size.width - area.origin.x - 18;
4330
36bb2ca2 4331 area.size.height = [UISearchField defaultHeight];
3178d79b 4332
36bb2ca2 4333 field_ = [[UISearchField alloc] initWithFrame:area];
b4d89997 4334
36bb2ca2
JF
4335 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
4336 [field_ setFont:font];
4337 CFRelease(font);
b4d89997 4338
36bb2ca2 4339 [field_ setPlaceholder:@"Package Names & Descriptions"];
36bb2ca2 4340 [field_ setDelegate:self];
b4d89997 4341
8cc774c5
JF
4342#ifdef __OBJC2__
4343 [field_ setPaddingTop:3];
4344#else
4345 [field_ setPaddingTop:5];
4346#endif
4347
36bb2ca2
JF
4348#ifndef __OBJC2__
4349 UITextTraits *traits = [field_ textTraits];
4350 [traits setEditingDelegate:self];
4351 [traits setReturnKeyType:6];
4352 [traits setAutoCapsType:0];
4353 [traits setAutoCorrectionType:1];
4354#endif
b4d89997 4355
8cc774c5
JF
4356 CGRect accrect = {{0, 6}, {6 + cnfrect.size.width + 6 + area.size.width + 6, area.size.height + 30}};
4357
4358 accessory_ = [[UIView alloc] initWithFrame:accrect];
795d26fc
JF
4359 [accessory_ addSubview:field_];
4360
36bb2ca2
JF
4361 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
4362 [configure setShowPressFeedback:YES];
559dec34 4363 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
36bb2ca2 4364 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
36bb2ca2
JF
4365 [accessory_ addSubview:configure];
4366 } return self;
3178d79b
JF
4367}
4368
a0be02eb 4369- (void) flipPage {
199d0ba5 4370#ifndef __OBJC2__
a0be02eb
JF
4371 LKAnimation *animation = [LKTransition animation];
4372 [animation setType:@"oglFlip"];
4373 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
4374 [animation setFillMode:@"extended"];
4375 [animation setTransitionFlags:3];
4376 [animation setDuration:10];
4377 [animation setSpeed:0.35];
7fea16f2
JF
4378 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
4379 [[transition_ _layer] addAnimation:animation forKey:0];
a0be02eb
JF
4380 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
4381 flipped_ = !flipped_;
199d0ba5 4382#endif
a0be02eb
JF
4383}
4384
36bb2ca2 4385- (void) configurePushed {
a0be02eb
JF
4386 [field_ resignFirstResponder];
4387 [self flipPage];
4388}
4389
4390- (void) resetViewAnimated:(BOOL)animated {
4391 if (flipped_)
4392 [self flipPage];
4393 [table_ resetViewAnimated:animated];
8fe19fc1
JF
4394}
4395
36bb2ca2 4396- (void) reloadData {
a0be02eb
JF
4397 if (flipped_)
4398 [self flipPage];
4399 [table_ setObject:[field_ text]];
4400 [table_ reloadData];
4401 [table_ resetCursor];
3178d79b
JF
4402}
4403
36bb2ca2
JF
4404- (UIView *) accessoryView {
4405 return accessory_;
4406}
b4d89997 4407
a0be02eb
JF
4408- (NSString *) title {
4409 return nil;
4410}
4411
36bb2ca2
JF
4412- (NSString *) backButtonTitle {
4413 return @"Search";
4414}
74127566 4415
51e95165
JF
4416- (void) setDelegate:(id)delegate {
4417 [table_ setDelegate:delegate];
4418 [super setDelegate:delegate];
4419}
4420
3178d79b 4421@end
36bb2ca2 4422/* }}} */
3178d79b 4423
36bb2ca2 4424@implementation CYBook
b4d89997 4425
36bb2ca2
JF
4426- (void) dealloc {
4427 [overlay_ release];
4428 [indicator_ release];
4429 [prompt_ release];
4430 [progress_ release];
4431 [super dealloc];
4432}
b4d89997 4433
7e9a36b6
JF
4434- (NSString *) getTitleForPage:(RVPage *)page {
4435 return Simplify([super getTitleForPage:page]);
4436}
4437
9a7b04c5
JF
4438- (BOOL) updating {
4439 return updating_;
4440}
4441
36bb2ca2 4442- (void) update {
9a7b04c5 4443 [navbar_ setPrompt:@""];
36bb2ca2
JF
4444 [navbar_ addSubview:overlay_];
4445 [indicator_ startAnimation];
4446 [prompt_ setText:@"Updating Database..."];
4447 [progress_ setProgress:0];
b4d89997 4448
9a7b04c5
JF
4449 updating_ = true;
4450
36bb2ca2
JF
4451 [NSThread
4452 detachNewThreadSelector:@selector(_update)
4453 toTarget:self
4454 withObject:nil
4455 ];
3178d79b
JF
4456}
4457
36bb2ca2 4458- (void) _update_ {
9a7b04c5
JF
4459 updating_ = false;
4460
36bb2ca2
JF
4461 [overlay_ removeFromSuperview];
4462 [indicator_ stopAnimation];
4463 [delegate_ reloadData];
b4d89997 4464
36bb2ca2 4465 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3178d79b
JF
4466}
4467
36bb2ca2
JF
4468- (id) initWithFrame:(CGRect)frame database:(Database *)database {
4469 if ((self = [super initWithFrame:frame]) != nil) {
4470 database_ = database;
3178d79b 4471
2388b078
JF
4472 if (Advanced_)
4473 [navbar_ setBarStyle:1];
4474
36bb2ca2 4475 CGRect ovrrect = [navbar_ bounds];
8cc774c5 4476 ovrrect.size.height = ([UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height);
3178d79b 4477
36bb2ca2 4478 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
b4d89997 4479
795d26fc
JF
4480 UIProgressIndicatorStyle style = Advanced_ ?
4481 kUIProgressIndicatorStyleSmallWhite :
4482 kUIProgressIndicatorStyleSmallBlack;
4483
4484 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:style];
36bb2ca2
JF
4485 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
4486 CGRect indrect = {{indoffset, indoffset}, indsize};
8fe19fc1 4487
36bb2ca2 4488 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
795d26fc 4489 [indicator_ setStyle:style];
36bb2ca2 4490 [overlay_ addSubview:indicator_];
b4d89997 4491
8cc774c5 4492 CGSize prmsize = {200, indsize.width + 4};
b4d89997 4493
36bb2ca2
JF
4494 CGRect prmrect = {{
4495 indoffset * 2 + indsize.width,
8cc774c5
JF
4496#ifdef __OBJC2__
4497 -1 +
4498#endif
36bb2ca2
JF
4499 (ovrrect.size.height - prmsize.height) / 2
4500 }, prmsize};
b4d89997 4501
36bb2ca2 4502 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
b4d89997 4503
36bb2ca2 4504 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
b4d89997 4505
77fcccaf 4506 [prompt_ setColor:(Advanced_ ? White_ : Blueish_)];
36bb2ca2
JF
4507 [prompt_ setBackgroundColor:Clear_];
4508 [prompt_ setFont:font];
b4d89997 4509
36bb2ca2 4510 CFRelease(font);
b4d89997 4511
36bb2ca2 4512 [overlay_ addSubview:prompt_];
b4d89997 4513
36bb2ca2 4514 CGSize prgsize = {75, 100};
b4d89997 4515
36bb2ca2
JF
4516 CGRect prgrect = {{
4517 ovrrect.size.width - prgsize.width - 10,
4518 (ovrrect.size.height - prgsize.height) / 2
4519 } , prgsize};
4520
4521 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
4522 [progress_ setStyle:0];
4523 [overlay_ addSubview:progress_];
b4d89997 4524 } return self;
3178d79b
JF
4525}
4526
36bb2ca2
JF
4527- (void) _update {
4528 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
dc5812ec 4529
36bb2ca2
JF
4530 Status status;
4531 status.setDelegate(self);
b4d89997 4532
36bb2ca2 4533 [database_ updateWithStatus:status];
3178d79b 4534
36bb2ca2
JF
4535 [self
4536 performSelectorOnMainThread:@selector(_update_)
4537 withObject:nil
4538 waitUntilDone:NO
4539 ];
b4d89997 4540
36bb2ca2 4541 [pool release];
dc5812ec
JF
4542}
4543
31f3cfff
JF
4544- (void) setProgressError:(NSString *)error forPackage:(NSString *)id {
4545 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
dc5812ec
JF
4546}
4547
36bb2ca2
JF
4548- (void) setProgressTitle:(NSString *)title {
4549 [self
4550 performSelectorOnMainThread:@selector(_setProgressTitle:)
4551 withObject:title
4552 waitUntilDone:YES
4553 ];
3178d79b
JF
4554}
4555
36bb2ca2
JF
4556- (void) setProgressPercent:(float)percent {
4557}
b4d89997 4558
36bb2ca2
JF
4559- (void) addProgressOutput:(NSString *)output {
4560 [self
4561 performSelectorOnMainThread:@selector(_addProgressOutput:)
4562 withObject:output
4563 waitUntilDone:YES
4564 ];
4565}
b4d89997 4566
36bb2ca2
JF
4567- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4568 [sheet dismiss];
4569}
4570
36bb2ca2
JF
4571- (void) _setProgressTitle:(NSString *)title {
4572 [prompt_ setText:[title stringByAppendingString:@"..."]];
4573}
b4d89997 4574
36bb2ca2 4575- (void) _addProgressOutput:(NSString *)output {
8fe19fc1
JF
4576}
4577
3178d79b 4578@end
3178d79b 4579
2367a917 4580@interface Cydia : UIApplication <
3178d79b 4581 ConfirmationViewDelegate,
b4d89997 4582 ProgressViewDelegate,
5f6bff8c
JF
4583 SearchViewDelegate,
4584 CydiaDelegate
2367a917 4585> {
dc5812ec 4586 UIWindow *window_;
36bb2ca2 4587
3178d79b
JF
4588 UIView *underlay_;
4589 UIView *overlay_;
36bb2ca2 4590 CYBook *book_;
2367a917 4591 UIButtonBar *buttonbar_;
3178d79b 4592
3178d79b 4593 ConfirmationView *confirm_;
9bedffaa 4594
a54b1c10 4595 NSMutableArray *essential_;
9bedffaa 4596 NSMutableArray *broken_;
dc5812ec
JF
4597
4598 Database *database_;
4599 ProgressView *progress_;
4600
06aa974d 4601 unsigned tag_;
b4d89997
JF
4602
4603 UIKeyboard *keyboard_;
9b619239
JF
4604
4605 InstallView *install_;
4606 ChangesView *changes_;
4607 ManageView *manage_;
4608 SearchView *search_;
dc5812ec
JF
4609}
4610
dc5812ec
JF
4611@end
4612
4613@implementation Cydia
4614
9bedffaa
JF
4615- (void) _loaded {
4616 if ([broken_ count] != 0) {
4617 int count = [broken_ count];
4618
4619 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4620 initWithTitle:[NSString stringWithFormat:@"%d Half-Installed Package%@", count, (count == 1 ? @"" : @"s")]
4621 buttons:[NSArray arrayWithObjects:
4622 @"Forcibly Clear",
4623 @"Ignore (Temporary)",
4624 nil]
4625 defaultButtonIndex:0
4626 delegate:self
4627 context:@"fixhalf"
4628 ] autorelease];
4629
31f3cfff 4630 [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
4631 [sheet popupAlertAnimated:YES];
4632 } else if (!Ignored_ && [essential_ count] != 0) {
4633 int count = [essential_ count];
4634
4635 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4636 initWithTitle:[NSString stringWithFormat:@"%d Essential Upgrade%@", count, (count == 1 ? @"" : @"s")]
4637 buttons:[NSArray arrayWithObjects:@"Upgrade Essential", @"Ignore (Temporary)", nil]
4638 defaultButtonIndex:0
4639 delegate:self
4640 context:@"upgrade"
4641 ] autorelease];
4642
31f3cfff 4643 [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
4644 [sheet popupAlertAnimated:YES];
4645 }
4646}
4647
36bb2ca2
JF
4648- (void) _reloadData {
4649 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4650 [hud setText:@"Reloading Data"];
4651 [overlay_ addSubview:hud];
4652 [hud show:YES];*/
dc5812ec 4653
dc5812ec 4654 [database_ reloadData];
c25a610d 4655
b4d89997 4656 if (Packages_ == nil) {
bbb879fb 4657 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:128];
b4d89997
JF
4658 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4659 }
4660
36bb2ca2 4661 size_t changes(0);
9bedffaa 4662
a54b1c10 4663 [essential_ removeAllObjects];
9bedffaa 4664 [broken_ removeAllObjects];
b4d89997 4665
36bb2ca2
JF
4666 NSArray *packages = [database_ packages];
4667 for (int i(0), e([packages count]); i != e; ++i) {
4668 Package *package = [packages objectAtIndex:i];
9bedffaa
JF
4669 if ([package half])
4670 [broken_ addObject:package];
31f3cfff 4671 if ([package upgradableAndEssential:NO]) {
a54b1c10
JF
4672 if ([package essential])
4673 [essential_ addObject:package];
36bb2ca2 4674 ++changes;
a54b1c10 4675 }
36bb2ca2 4676 }
b4d89997 4677
36bb2ca2
JF
4678 if (changes != 0) {
4679 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
c25a610d 4680 [buttonbar_ setBadgeValue:badge forButton:3];
9e98e020
JF
4681 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4682 [buttonbar_ setBadgeAnimated:YES forButton:3];
c25a610d
JF
4683 [self setApplicationBadge:badge];
4684 } else {
4685 [buttonbar_ setBadgeValue:nil forButton:3];
9e98e020
JF
4686 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4687 [buttonbar_ setBadgeAnimated:NO forButton:3];
c25a610d
JF
4688 [self removeApplicationBadge];
4689 }
b4d89997 4690
bbb879fb
JF
4691 if (Changed_) {
4692 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
4693 Changed_ = false;
4694 }
36bb2ca2 4695
9b619239
JF
4696 /* XXX: this is just stupid */
4697 if (tag_ != 2)
4698 [install_ reloadData];
4699 if (tag_ != 3)
4700 [changes_ reloadData];
4701 if (tag_ != 4)
4702 [manage_ reloadData];
4703 if (tag_ != 5)
4704 [search_ reloadData];
4705
36bb2ca2 4706 [book_ reloadData];
bbb879fb 4707
5a09ae08 4708 if ([packages count] == 0);
9bedffaa
JF
4709 else if (Loaded_)
4710 [self _loaded];
4711 else {
a54b1c10 4712 Loaded_ = YES;
5a09ae08 4713 [book_ update];
a54b1c10
JF
4714 }
4715
36bb2ca2
JF
4716 /*[hud show:NO];
4717 [hud removeFromSuperview];*/
b4d89997
JF
4718}
4719
36bb2ca2
JF
4720- (void) reloadData {
4721 @synchronized (self) {
4722 if (confirm_ == nil)
4723 [self _reloadData];
4724 }
b4d89997
JF
4725}
4726
4727- (void) resolve {
4728 pkgProblemResolver *resolver = [database_ resolver];
4729
4730 resolver->InstallProtect();
4731 if (!resolver->Resolve(true))
4732 _error->Discard();
2367a917
JF
4733}
4734
4735- (void) perform {
8fe19fc1 4736 [database_ prepare];
49048579
JF
4737
4738 if ([database_ cache]->BrokenCount() == 0)
4739 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4740 else {
4741 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4742 NSArray *packages = [database_ packages];
4743
4744 for (size_t i(0); i != [packages count]; ++i) {
4745 Package *package = [packages objectAtIndex:i];
4746 if ([package broken])
4747 [broken addObject:[package name]];
4748 }
4749
4750 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4751 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4752 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4753 defaultButtonIndex:0
4754 delegate:self
a54b1c10 4755 context:@"broken"
49048579
JF
4756 ] autorelease];
4757
4758 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4759 [sheet popupAlertAnimated:YES];
4760
36bb2ca2 4761 [self _reloadData];
49048579 4762 }
3178d79b
JF
4763}
4764
36bb2ca2
JF
4765- (void) installPackage:(Package *)package {
4766 @synchronized (self) {
4767 [package install];
4768 [self resolve];
4769 [self perform];
4770 }
4771}
4772
4773- (void) removePackage:(Package *)package {
4774 @synchronized (self) {
4775 [package remove];
4776 [self resolve];
4777 [self perform];
4778 }
4779}
4780
4781- (void) distUpgrade {
4782 @synchronized (self) {
4783 [database_ upgrade];
4784 [self perform];
4785 }
b4d89997
JF
4786}
4787
3178d79b 4788- (void) cancel {
36bb2ca2
JF
4789 @synchronized (self) {
4790 [confirm_ release];
4791 confirm_ = nil;
4792 [self _reloadData];
4793 }
3178d79b
JF
4794}
4795
4796- (void) confirm {
4797 [overlay_ removeFromSuperview];
77fcccaf 4798 reload_ = true;
3178d79b 4799
2367a917
JF
4800 [progress_
4801 detachNewThreadSelector:@selector(perform)
4802 toTarget:database_
4803 withObject:nil
b4d89997 4804 title:@"Running..."
2367a917
JF
4805 ];
4806}
4807
b31b87cc
JF
4808- (void) bootstrap_ {
4809 [database_ update];
4810 [database_ upgrade];
4811 [database_ prepare];
4812 [database_ perform];
4813}
4814
4815- (void) bootstrap {
4816 [progress_
4817 detachNewThreadSelector:@selector(bootstrap_)
4818 toTarget:self
4819 withObject:nil
4820 title:@"Bootstrap Install..."
4821 ];
4822}
4823
2367a917 4824- (void) progressViewIsComplete:(ProgressView *)progress {
36bb2ca2
JF
4825 @synchronized (self) {
4826 [self _reloadData];
4827
4828 if (confirm_ != nil) {
4829 [underlay_ addSubview:overlay_];
4830 [confirm_ removeFromSuperview];
4831 [confirm_ release];
4832 confirm_ = nil;
4833 }
3178d79b 4834 }
dc5812ec
JF
4835}
4836
36bb2ca2 4837- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
a54b1c10 4838 NSString *context = [sheet context];
9bedffaa
JF
4839 if ([context isEqualToString:@"fixhalf"])
4840 switch (button) {
4841 case 1:
4842 @synchronized (self) {
4843 for (int i = 0, e = [broken_ count]; i != e; ++i) {
4844 Package *broken = [broken_ objectAtIndex:i];
4845 [broken remove];
4846
4847 NSString *id = [broken id];
4848 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.prerm", id] UTF8String]);
4849 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postrm", id] UTF8String]);
4850 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.preinst", id] UTF8String]);
4851 unlink([[NSString stringWithFormat:@"/var/lib/dpkg/info/%@.postinst", id] UTF8String]);
4852 }
4853
4854 [self resolve];
4855 [self perform];
4856 }
4857 break;
4858
4859 case 2:
4860 [broken_ removeAllObjects];
4861 [self _loaded];
4862 break;
4863
4864 default:
4865 _assert(false);
4866 }
4867 else if ([context isEqualToString:@"upgrade"])
a54b1c10
JF
4868 switch (button) {
4869 case 1:
4870 @synchronized (self) {
4871 for (int i = 0, e = [essential_ count]; i != e; ++i) {
4872 Package *essential = [essential_ objectAtIndex:i];
4873 [essential install];
4874 }
4875
4876 [self resolve];
4877 [self perform];
4878 }
4879 break;
4880
4881 case 2:
4882 Ignored_ = YES;
4883 break;
4884
9bedffaa
JF
4885 default:
4886 _assert(false);
a54b1c10
JF
4887 }
4888
36bb2ca2
JF
4889 [sheet dismiss];
4890}
2367a917 4891
36bb2ca2 4892- (void) setPage:(RVPage *)page {
9b619239 4893 [page resetViewAnimated:NO];
36bb2ca2
JF
4894 [page setDelegate:self];
4895 [book_ setPage:page];
dc5812ec
JF
4896}
4897
9b619239 4898- (RVPage *) _setHomePage {
36bb2ca2
JF
4899 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4900 [self setPage:browser];
4901 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4902 return browser;
2367a917
JF
4903}
4904
dc5812ec 4905- (void) buttonBarItemTapped:(id)sender {
06aa974d 4906 unsigned tag = [sender tag];
559dec34
JF
4907 if (tag == tag_) {
4908 [book_ resetViewAnimated:YES];
4909 return;
4910 }
dc5812ec 4911
06aa974d 4912 switch (tag) {
11108eb6 4913 case 1: [self _setHomePage]; break;
36bb2ca2 4914
11108eb6
JF
4915 case 2: [self setPage:install_]; break;
4916 case 3: [self setPage:changes_]; break;
4917 case 4: [self setPage:manage_]; break;
4918 case 5: [self setPage:search_]; break;
36bb2ca2 4919
11108eb6 4920 default: _assert(false);
dc5812ec
JF
4921 }
4922
06aa974d 4923 tag_ = tag;
06aa974d
JF
4924}
4925
b4d89997 4926- (void) applicationWillSuspend {
36bb2ca2
JF
4927 [super applicationWillSuspend];
4928
77fcccaf
JF
4929 [database_ clean];
4930
4931 if (reload_) {
4932 pid_t pid = ExecFork();
4933 if (pid == 0) {
4934 sleep(1);
31f3cfff
JF
4935 if (pid_t child = fork())
4936 waitpid(child, NULL, 0);
4937 else {
4938 execlp("launchctl", "launchctl", "unload", SpringBoard_, NULL);
4939 exit(0);
4940 }
4941 execlp("launchctl", "launchctl", "load", SpringBoard_, NULL);
77fcccaf
JF
4942 exit(0);
4943 }
4944 }
b4d89997
JF
4945}
4946
dc5812ec
JF
4947- (void) applicationDidFinishLaunching:(id)unused {
4948 _assert(pkgInitConfig(*_config));
4949 _assert(pkgInitSystem(*_config, _system));
4950
3178d79b 4951 confirm_ = nil;
06aa974d 4952 tag_ = 1;
3178d79b 4953
a54b1c10 4954 essential_ = [[NSMutableArray alloc] initWithCapacity:4];
9bedffaa 4955 broken_ = [[NSMutableArray alloc] initWithCapacity:4];
a54b1c10 4956
dc5812ec
JF
4957 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4958 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4959
4960 [window_ orderFront: self];
4961 [window_ makeKey: self];
4962 [window_ _setHidden: NO];
4963
8b29f8e6
JF
4964 database_ = [[Database alloc] init];
4965 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] database:database_ delegate:self];
4966 [database_ setDelegate:progress_];
dc5812ec
JF
4967 [window_ setContentView:progress_];
4968
3178d79b
JF
4969 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4970 [progress_ setContentView:underlay_];
4971
4972 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
b31b87cc
JF
4973
4974 if (!bootstrap_)
4975 [underlay_ addSubview:overlay_];
dc5812ec 4976
36bb2ca2
JF
4977 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4978 0, 0, screenrect.size.width, screenrect.size.height - 48
4979 ) database:database_];
dc5812ec 4980
36bb2ca2 4981 [book_ setDelegate:self];
dc5812ec 4982
36bb2ca2 4983 [overlay_ addSubview:book_];
b19871dd 4984
dc5812ec
JF
4985 NSArray *buttonitems = [NSArray arrayWithObjects:
4986 [NSDictionary dictionaryWithObjectsAndKeys:
4987 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
9b619239
JF
4988 @"home-up.png", kUIButtonBarButtonInfo,
4989 @"home-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
4990 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4991 self, kUIButtonBarButtonTarget,
9b619239 4992 @"Home", kUIButtonBarButtonTitle,
dc5812ec
JF
4993 @"0", kUIButtonBarButtonType,
4994 nil],
4995
4996 [NSDictionary dictionaryWithObjectsAndKeys:
4997 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4998 @"install-up.png", kUIButtonBarButtonInfo,
4999 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
5000 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
5001 self, kUIButtonBarButtonTarget,
5002 @"Install", kUIButtonBarButtonTitle,
5003 @"0", kUIButtonBarButtonType,
5004 nil],
5005
5006 [NSDictionary dictionaryWithObjectsAndKeys:
5007 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
5008 @"changes-up.png", kUIButtonBarButtonInfo,
5009 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
5010 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
5011 self, kUIButtonBarButtonTarget,
b4d89997 5012 @"Changes", kUIButtonBarButtonTitle,
dc5812ec
JF
5013 @"0", kUIButtonBarButtonType,
5014 nil],
5015
5016 [NSDictionary dictionaryWithObjectsAndKeys:
5017 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
5018 @"manage-up.png", kUIButtonBarButtonInfo,
5019 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
5020 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
5021 self, kUIButtonBarButtonTarget,
36bb2ca2 5022 @"Manage", kUIButtonBarButtonTitle,
dc5812ec
JF
5023 @"0", kUIButtonBarButtonType,
5024 nil],
5025
5026 [NSDictionary dictionaryWithObjectsAndKeys:
5027 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
5028 @"search-up.png", kUIButtonBarButtonInfo,
5029 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
5030 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
5031 self, kUIButtonBarButtonTarget,
b4d89997 5032 @"Search", kUIButtonBarButtonTitle,
dc5812ec
JF
5033 @"0", kUIButtonBarButtonType,
5034 nil],
5035 nil];
5036
2367a917 5037 buttonbar_ = [[UIButtonBar alloc]
3178d79b 5038 initInView:overlay_
dc5812ec 5039 withFrame:CGRectMake(
2388b078
JF
5040 0, screenrect.size.height - ButtonBarHeight_,
5041 screenrect.size.width, ButtonBarHeight_
dc5812ec
JF
5042 )
5043 withItemList:buttonitems
5044 ];
5045
2367a917
JF
5046 [buttonbar_ setDelegate:self];
5047 [buttonbar_ setBarStyle:1];
5048 [buttonbar_ setButtonBarTrackingMode:2];
dc5812ec
JF
5049
5050 int buttons[5] = {1, 2, 3, 4, 5};
2367a917
JF
5051 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
5052 [buttonbar_ showButtonGroup:0 withDuration:0];
dc5812ec
JF
5053
5054 for (int i = 0; i != 5; ++i)
2367a917 5055 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
2388b078 5056 i * 64 + 2, 1, 60, ButtonBarHeight_
dc5812ec
JF
5057 )];
5058
2367a917 5059 [buttonbar_ showSelectionForButton:1];
3178d79b 5060 [overlay_ addSubview:buttonbar_];
dc5812ec 5061
b4d89997 5062 [UIKeyboard initImplementationNow];
b4d89997 5063 CGSize keysize = [UIKeyboard defaultSize];
ab398adf 5064 CGRect keyrect = {{0, [overlay_ bounds].size.height}, keysize};
b4d89997 5065 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
d73cede2 5066 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
ab398adf 5067 [overlay_ addSubview:keyboard_];
b4d89997 5068
9b619239
JF
5069 install_ = [[InstallView alloc] initWithBook:book_ database:database_];
5070 changes_ = [[ChangesView alloc] initWithBook:book_ database:database_];
5071 manage_ = [[ManageView alloc] initWithBook:book_ database:database_];
5072 search_ = [[SearchView alloc] initWithBook:book_ database:database_];
5073
b31b87cc 5074 [progress_ resetView];
5a09ae08 5075 [self reloadData];
b31b87cc
JF
5076
5077 if (bootstrap_)
5078 [self bootstrap];
36bb2ca2 5079 else
9b619239 5080 [self _setHomePage];
b4d89997
JF
5081}
5082
5083- (void) showKeyboard:(BOOL)show {
ab398adf
JF
5084 CGSize keysize = [UIKeyboard defaultSize];
5085 CGRect keydown = {{0, [overlay_ bounds].size.height}, keysize};
5086 CGRect keyup = keydown;
5087 keyup.origin.y -= keysize.height;
5088
5089 UIFrameAnimation *animation = [[[UIFrameAnimation alloc] initWithTarget:keyboard_] autorelease];
5090 [animation setSignificantRectFields:2];
5091
5092 if (show) {
5093 [animation setStartFrame:keydown];
5094 [animation setEndFrame:keyup];
5095 [keyboard_ activate];
5096 } else {
5097 [animation setStartFrame:keyup];
5098 [animation setEndFrame:keydown];
5099 [keyboard_ deactivate];
5100 }
5101
5102 [[UIAnimator sharedAnimator]
5103 addAnimations:[NSArray arrayWithObjects:animation, nil]
2388b078 5104 withDuration:KeyboardTime_
ab398adf
JF
5105 start:YES
5106 ];
dc5812ec
JF
5107}
5108
36bb2ca2 5109- (void) slideUp:(UIAlertSheet *)alert {
77fcccaf
JF
5110 if (Advanced_)
5111 [alert presentSheetFromButtonBar:buttonbar_];
5112 else
5113 [alert presentSheetInView:overlay_];
36bb2ca2
JF
5114}
5115
dc5812ec
JF
5116@end
5117
b4d89997
JF
5118void AddPreferences(NSString *plist) {
5119 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5120
5121 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
5122 _assert(settings != NULL);
5123 NSMutableArray *items = [settings objectForKey:@"items"];
5124
5125 bool cydia(false);
5126
5127 for (size_t i(0); i != [items count]; ++i) {
5128 NSMutableDictionary *item([items objectAtIndex:i]);
5129 NSString *label = [item objectForKey:@"label"];
5130 if (label != nil && [label isEqualToString:@"Cydia"]) {
5131 cydia = true;
5132 break;
5133 }
5134 }
5135
5136 if (!cydia) {
5137 for (size_t i(0); i != [items count]; ++i) {
5138 NSDictionary *item([items objectAtIndex:i]);
5139 NSString *label = [item objectForKey:@"label"];
5140 if (label != nil && [label isEqualToString:@"General"]) {
5141 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
5142 @"CydiaSettings", @"bundle",
5143 @"PSLinkCell", @"cell",
5144 [NSNumber numberWithBool:YES], @"hasIcon",
5145 [NSNumber numberWithBool:YES], @"isController",
5146 @"Cydia", @"label",
5147 nil] atIndex:(i + 1)];
5148
5149 break;
5150 }
5151 }
5152
5153 _assert([settings writeToFile:plist atomically:YES] == YES);
5154 }
5155
5156 [pool release];
5157}
5158
5159/*IMP alloc_;
5160id Alloc_(id self, SEL selector) {
5161 id object = alloc_(self, selector);
5162 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
5163 return object;
5164}*/
5165
36bb2ca2
JF
5166/*IMP dealloc_;
5167id Dealloc_(id self, SEL selector) {
5168 id object = dealloc_(self, selector);
5169 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
5170 return object;
5171}*/
b4d89997 5172
36bb2ca2 5173int main(int argc, char *argv[]) {
d73cede2
JF
5174 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
5175
b31b87cc
JF
5176 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
5177
d73cede2
JF
5178 Home_ = NSHomeDirectory();
5179
5180 {
5181 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
5182 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
5183 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
5184 Sounds_Keyboard_ = [keyboard boolValue];
5185 }
5186
b4d89997
JF
5187 setuid(0);
5188 setgid(0);
5189
5190 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
5191 alloc_ = alloc->method_imp;
5192 alloc->method_imp = (IMP) &Alloc_;*/
5193
36bb2ca2
JF
5194 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
5195 dealloc_ = dealloc->method_imp;
5196 dealloc->method_imp = (IMP) &Dealloc_;*/
5197
2a8d9add
JF
5198 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
5199 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5f6bff8c 5200 Firmware_ = strdup([prover UTF8String]);
2a8d9add
JF
5201 NSArray *versions = [prover componentsSeparatedByString:@"."];
5202 int count = [versions count];
5203 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
51a1ffdf
JF
5204 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
5205 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
2a8d9add
JF
5206 }
5207 }
5208
3178d79b
JF
5209 size_t size;
5210 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
5211 char *machine = new char[size];
5212 sysctlbyname("hw.machine", machine, &size, NULL, 0);
5213 Machine_ = machine;
5214
5215 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
5216 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
5217 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
5218 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
5219 CFRelease(serial);
5220 }
5221
5222 IOObjectRelease(service);
5223 }
5224
99364ec4
JF
5225 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
5226 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
b4d89997
JF
5227
5228 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
5229 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
5230 else
5231 Packages_ = [Metadata_ objectForKey:@"Packages"];
5232
26c2dd8c
JF
5233 if (access("/User", F_OK) != 0)
5234 system("/usr/libexec/cydia/firmware.sh");
9e98e020 5235
199d0ba5 5236 Locale_ = CFLocaleCopyCurrent();
36bb2ca2
JF
5237 space_ = CGColorSpaceCreateDeviceRGB();
5238
77fcccaf 5239 Blueish_.Set(space_, 0x19/255.f, 0x32/255.f, 0x50/255.f, 1.0);
36bb2ca2
JF
5240 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
5241 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
5242 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
5243 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
5244
5245 int value = UIApplicationMain(argc, argv, [Cydia class]);
5246
5247 CGColorSpaceRelease(space_);
199d0ba5 5248 CFRelease(Locale_);
36bb2ca2 5249
dc5812ec 5250 [pool release];
36bb2ca2 5251 return value;
dc5812ec 5252}