]> git.saurik.com Git - cydia.git/blame - Cydia.mm
Some mild naming and transition nits from the #iphone gang.
[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 {{{ */
5f6bff8c 39#include <CoreGraphics/CoreGraphics.h>
9743c015 40#include <GraphicsServices/GraphicsServices.h>
5f6bff8c
JF
41#include <Foundation/Foundation.h>
42#include <UIKit/UIKit.h>
36bb2ca2
JF
43#include <WebCore/DOMHTML.h>
44
45#include <WebKit/WebFrame.h>
46#include <WebKit/WebView.h>
9743c015
JF
47
48#include <objc/objc.h>
1e7a90f5 49#include <objc/runtime.h>
dc5812ec 50
2367a917 51#include <sstream>
a0be02eb
JF
52#include <string>
53
2367a917
JF
54#include <ext/stdio_filebuf.h>
55
dc5812ec
JF
56#include <apt-pkg/acquire.h>
57#include <apt-pkg/acquire-item.h>
58#include <apt-pkg/algorithms.h>
59#include <apt-pkg/cachefile.h>
60#include <apt-pkg/configuration.h>
3178d79b 61#include <apt-pkg/debmetaindex.h>
dc5812ec
JF
62#include <apt-pkg/error.h>
63#include <apt-pkg/init.h>
64#include <apt-pkg/pkgrecords.h>
65#include <apt-pkg/sourcelist.h>
2367a917 66#include <apt-pkg/sptr.h>
dc5812ec 67
3178d79b 68#include <sys/sysctl.h>
2a8d9add 69#include <notify.h>
3178d79b 70
b4d89997
JF
71extern "C" {
72#include <mach-o/nlist.h>
73}
74
a0be02eb
JF
75#include <cstdio>
76#include <cstdlib>
77#include <cstring>
b4d89997 78
2367a917 79#include <errno.h>
dc5812ec 80#include <pcre.h>
dc5812ec
JF
81/* }}} */
82/* Extension Keywords {{{ */
2367a917 83#define _trace() fprintf(stderr, "_trace()@%s:%u[%s]\n", __FILE__, __LINE__, __FUNCTION__)
dc5812ec
JF
84
85#define _assert(test) do \
86 if (!(test)) { \
2367a917 87 fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
dc5812ec
JF
88 exit(-1); \
89 } \
90while (false)
68a238ec
JF
91
92#define _not(type) ((type) ~ (type) 0)
36bb2ca2
JF
93
94#define _transient
dc5812ec 95/* }}} */
3178d79b 96
36bb2ca2 97/* Miscellaneous Messages {{{ */
b4d89997
JF
98@interface NSString (Cydia)
99- (NSString *) stringByAddingPercentEscapes;
100- (NSString *) stringByReplacingCharacter:(unsigned short)arg0 withCharacter:(unsigned short)arg1;
101@end
102/* }}} */
36bb2ca2
JF
103/* External Constants {{{ */
104extern NSString *kUIButtonBarButtonAction;
105extern NSString *kUIButtonBarButtonInfo;
106extern NSString *kUIButtonBarButtonInfoOffset;
107extern NSString *kUIButtonBarButtonSelectedInfo;
108extern NSString *kUIButtonBarButtonStyle;
109extern NSString *kUIButtonBarButtonTag;
110extern NSString *kUIButtonBarButtonTarget;
111extern NSString *kUIButtonBarButtonTitle;
112extern NSString *kUIButtonBarButtonTitleVerticalHeight;
113extern NSString *kUIButtonBarButtonTitleWidth;
114extern NSString *kUIButtonBarButtonType;
115/* }}} */
116
117#if 1
118#define $_
119#define _$
120#else
121#define $_ fprintf(stderr, "+");_trace();
122#define _$ fprintf(stderr, "-");_trace();
123#endif
b4d89997 124
1e7a90f5
JF
125/* iPhoneOS 2.0 Compatibility {{{ */
126#ifdef __OBJC2__
127@interface UICGColor : NSObject {
128}
129
130- (id) initWithCGColor:(CGColorRef)color;
131@end
132
133@interface UIFont {
134}
135
136- (UIFont *) fontWithSize:(CGFloat)size;
137@end
138
139@interface NSObject (iPhoneOS)
140- (CGColorRef) cgColor;
ffdee4ec 141- (CGColorRef) CGColor;
1e7a90f5
JF
142- (void) set;
143@end
144
145@implementation NSObject (iPhoneOS)
146
147- (CGColorRef) cgColor {
ffdee4ec
JF
148 return [self CGColor];
149}
150
151- (CGColorRef) CGColor {
1e7a90f5
JF
152 return (CGColorRef) self;
153}
154
155- (void) set {
ffdee4ec 156 [[[[objc_getClass("UICGColor") alloc] initWithCGColor:[self CGColor]] autorelease] set];
1e7a90f5
JF
157}
158
159@end
160
161@interface UITextView (iPhoneOS)
162- (void) setTextSize:(float)size;
163@end
164
165@implementation UITextView (iPhoneOS)
166
167- (void) setTextSize:(float)size {
168 [self setFont:[[self font] fontWithSize:size]];
169}
170
171@end
49048579 172#endif
1e7a90f5 173/* }}} */
49048579 174
9743c015
JF
175OBJC_EXPORT const char *class_getName(Class cls);
176
b4d89997 177/* Reset View (UIView) {{{ */
36bb2ca2 178@interface UIView (RVBook)
b4d89997 179- (void) resetViewAnimated:(BOOL)animated;
36bb2ca2 180- (void) clearView;
b4d89997
JF
181@end
182
36bb2ca2 183@implementation UIView (RVBook)
b4d89997
JF
184
185- (void) resetViewAnimated:(BOOL)animated {
9743c015 186 fprintf(stderr, "%s\n", class_getName(self->isa));
b4d89997
JF
187 _assert(false);
188}
189
36bb2ca2
JF
190- (void) clearView {
191 fprintf(stderr, "%s\n", class_getName(self->isa));
192 _assert(false);
193}
194
b4d89997
JF
195@end
196/* }}} */
197/* Reset View (UITable) {{{ */
36bb2ca2 198@interface UITable (RVBook)
b4d89997 199- (void) resetViewAnimated:(BOOL)animated;
36bb2ca2 200- (void) clearView;
b4d89997
JF
201@end
202
36bb2ca2 203@implementation UITable (RVBook)
b4d89997
JF
204
205- (void) resetViewAnimated:(BOOL)animated {
206 [self selectRow:-1 byExtendingSelection:NO withFade:animated];
207}
208
36bb2ca2
JF
209- (void) clearView {
210 [self clearAllData];
211}
212
b4d89997
JF
213@end
214/* }}} */
215/* Reset View (UISectionList) {{{ */
36bb2ca2 216@interface UISectionList (RVBook)
b4d89997 217- (void) resetViewAnimated:(BOOL)animated;
36bb2ca2 218- (void) clearView;
b4d89997
JF
219@end
220
36bb2ca2 221@implementation UISectionList (RVBook)
b4d89997
JF
222
223- (void) resetViewAnimated:(BOOL)animated {
224 [[self table] resetViewAnimated:animated];
225}
226
36bb2ca2
JF
227- (void) clearView {
228 [[self table] clearView];
229}
230
b4d89997
JF
231@end
232/* }}} */
233
234/* Perl-Compatible RegEx {{{ */
235class Pcre {
236 private:
237 pcre *code_;
238 pcre_extra *study_;
239 int capture_;
240 int *matches_;
241 const char *data_;
242
243 public:
244 Pcre(const char *regex) :
245 study_(NULL)
246 {
247 const char *error;
248 int offset;
249 code_ = pcre_compile(regex, 0, &error, &offset, NULL);
250
251 if (code_ == NULL) {
252 fprintf(stderr, "%d:%s\n", offset, error);
253 _assert(false);
254 }
255
256 pcre_fullinfo(code_, study_, PCRE_INFO_CAPTURECOUNT, &capture_);
257 matches_ = new int[(capture_ + 1) * 3];
258 }
259
260 ~Pcre() {
261 pcre_free(code_);
262 delete matches_;
263 }
264
265 NSString *operator [](size_t match) {
266 return [NSString
267 stringWithCString:(data_ + matches_[match * 2])
268 length:(matches_[match * 2 + 1] - matches_[match * 2])
269 ];
270 }
271
272 bool operator ()(const char *data, size_t size) {
273 data_ = data;
274 return pcre_exec(code_, study_, data, size, 0, 0, matches_, (capture_ + 1) * 3) >= 0;
275 }
276};
277/* }}} */
36bb2ca2
JF
278/* Mime Addresses {{{ */
279Pcre email_r("^\"?(.*)\"? <([^>]*)>$");
280
281@interface Address : NSObject {
282 NSString *name_;
283 NSString *email_;
284}
285
286- (NSString *) name;
287- (NSString *) email;
288
289+ (Address *) addressWithString:(NSString *)string;
290- (Address *) initWithString:(NSString *)string;
291@end
292
293@implementation Address
294
295- (void) dealloc {
296 [name_ release];
297 if (email_ != nil)
298 [email_ release];
299 [super dealloc];
300}
301
302- (NSString *) name {
303 return name_;
304}
305
306- (NSString *) email {
307 return email_;
308}
309
310+ (Address *) addressWithString:(NSString *)string {
311 return [[[Address alloc] initWithString:string] autorelease];
312}
313
314- (Address *) initWithString:(NSString *)string {
315 if ((self = [super init]) != nil) {
316 const char *data = [string UTF8String];
317 size_t size = [string length];
318
319 if (email_r(data, size)) {
320 name_ = [email_r[1] retain];
321 email_ = [email_r[2] retain];
322 } else {
323 name_ = [[NSString stringWithCString:data length:size] retain];
324 email_ = nil;
325 }
326 } return self;
327}
328
329@end
330/* }}} */
5f6bff8c 331/* CoreGraphics Primitives {{{ */
b4d89997
JF
332class CGColor {
333 private:
334 CGColorRef color_;
335
336 public:
36bb2ca2
JF
337 CGColor() :
338 color_(NULL)
339 {
340 }
341
342 CGColor(CGColorSpaceRef space, float red, float green, float blue, float alpha) :
343 color_(NULL)
344 {
345 Set(space, red, green, blue, alpha);
346 }
347
348 void Clear() {
349 if (color_ != NULL)
350 CGColorRelease(color_);
b4d89997
JF
351 }
352
353 ~CGColor() {
36bb2ca2
JF
354 Clear();
355 }
356
357 void Set(CGColorSpaceRef space, float red, float green, float blue, float alpha) {
358 Clear();
359 float color[] = {red, green, blue, alpha};
360 color_ = CGColorCreate(space, color);
b4d89997
JF
361 }
362
363 operator CGColorRef() {
364 return color_;
365 }
366};
367
368class GSFont {
369 private:
370 GSFontRef font_;
371
372 public:
373 ~GSFont() {
b4d89997
JF
374 CFRelease(font_);
375 }
376};
377/* }}} */
36bb2ca2
JF
378/* Right Alignment {{{ */
379@interface UIRightTextLabel : UITextLabel {
380 float _savedRightEdgeX;
381 BOOL _sizedtofit_flag;
382}
383
384- (void) setFrame:(CGRect)frame;
385- (void) setText:(NSString *)text;
386- (void) realignText;
387@end
388
389@implementation UIRightTextLabel
390
391- (void) setFrame:(CGRect)frame {
392 [super setFrame:frame];
393 if (_sizedtofit_flag == NO) {
394 _savedRightEdgeX = frame.origin.x;
395 [self realignText];
396 }
397}
398
399- (void) setText:(NSString *)text {
400 [super setText:text];
401 [self realignText];
402}
403
404- (void) realignText {
405 CGRect oldFrame = [self frame];
406
407 _sizedtofit_flag = YES;
408 [self sizeToFit]; // shrink down size so I can right align it
409
410 CGRect newFrame = [self frame];
411
412 oldFrame.origin.x = _savedRightEdgeX - newFrame.size.width;
413 oldFrame.size.width = newFrame.size.width;
414 [super setFrame:oldFrame];
415 _sizedtofit_flag = NO;
416}
417
418@end
419/* }}} */
b4d89997 420
36bb2ca2 421/* Random Global Variables {{{ */
3178d79b 422static const int PulseInterval_ = 50000;
2a8d9add 423
36bb2ca2
JF
424static CGColor Black_;
425static CGColor Clear_;
426static CGColor Red_;
427static CGColor White_;
428
d73cede2
JF
429static NSString *Home_;
430static BOOL Sounds_Keyboard_;
431
2a8d9add 432const char *Firmware_ = NULL;
3178d79b
JF
433const char *Machine_ = NULL;
434const char *SerialNumber_ = NULL;
435
2a8d9add
JF
436unsigned Major_;
437unsigned Minor_;
438unsigned BugFix_;
439
36bb2ca2
JF
440CGColorSpaceRef space_;
441
2a8d9add 442#define FW_LEAST(major, minor, bugfix) \
51a1ffdf
JF
443 (major < Major_ || major == Major_ && \
444 (minor < Minor_ || minor == Minor_ && \
445 bugfix <= BugFix_))
2a8d9add 446
36bb2ca2
JF
447bool bootstrap_;
448bool restart_;
b31b87cc 449
b4d89997
JF
450static NSMutableDictionary *Metadata_;
451static NSMutableDictionary *Packages_;
452static NSDate *now_;
8da60fb7 453
23672b39
JF
454NSString *GetLastUpdate() {
455 NSDate *update = [Metadata_ objectForKey:@"LastUpdate"];
456
457 if (update == nil)
458 return @"Never or Unknown";
459
460 CFLocaleRef locale = CFLocaleCopyCurrent();
461 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
462 CFStringRef formatted = CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) update);
463
464 CFRelease(formatter);
23672b39
JF
465 CFRelease(locale);
466
467 return [(NSString *) formatted autorelease];
468}
36bb2ca2
JF
469/* }}} */
470/* Display Helpers {{{ */
471inline float Interpolate(float begin, float end, float fraction) {
472 return (end - begin) * fraction + begin;
473}
2367a917 474
8fe19fc1
JF
475NSString *SizeString(double size) {
476 unsigned power = 0;
477 while (size > 1024) {
478 size /= 1024;
479 ++power;
480 }
481
482 static const char *powers_[] = {"B", "kB", "MB", "GB"};
483
484 return [NSString stringWithFormat:@"%.1f%s", size, powers_[power]];
485}
486
b4d89997
JF
487static const float TextViewOffset_ = 22;
488
489UITextView *GetTextView(NSString *value, float left, bool html) {
490 UITextView *text([[[UITextView alloc] initWithFrame:CGRectMake(left, 3, 310 - left, 1000)] autorelease]);
491 [text setEditable:NO];
492 [text setTextSize:16];
1e7a90f5 493 /*if (html)
b4d89997 494 [text setHTML:value];
1e7a90f5 495 else*/
b4d89997
JF
496 [text setText:value];
497 [text setEnabled:NO];
498
36bb2ca2 499 [text setBackgroundColor:Clear_];
b4d89997
JF
500
501 CGRect frame = [text frame];
502 [text setFrame:frame];
503 CGRect rect = [text visibleTextRect];
504 frame.size.height = rect.size.height;
505 [text setFrame:frame];
506
507 return text;
508}
4cf4165e
JF
509
510NSString *Simplify(NSString *title) {
511 const char *data = [title UTF8String];
512 size_t size = [title length];
513
514 Pcre title_r("^(.*?)( \\(.*\\))?$");
515 if (title_r(data, size))
516 return title_r[1];
517 else
518 return title;
519}
36bb2ca2
JF
520/* }}} */
521
522@class Package;
523@class Source;
524
525@protocol ProgressDelegate
526- (void) setProgressError:(NSString *)error;
527- (void) setProgressTitle:(NSString *)title;
528- (void) setProgressPercent:(float)percent;
529- (void) addProgressOutput:(NSString *)output;
530@end
531
532@protocol CydiaDelegate
533- (void) installPackage:(Package *)package;
534- (void) removePackage:(Package *)package;
535- (void) slideUp:(UIAlertSheet *)alert;
536- (void) distUpgrade;
537@end
b4d89997 538
dc5812ec
JF
539/* Status Delegation {{{ */
540class Status :
541 public pkgAcquireStatus
542{
543 private:
36bb2ca2 544 _transient id<ProgressDelegate> delegate_;
dc5812ec
JF
545
546 public:
547 Status() :
548 delegate_(nil)
549 {
550 }
551
552 void setDelegate(id delegate) {
553 delegate_ = delegate;
554 }
555
556 virtual bool MediaChange(std::string media, std::string drive) {
557 return false;
558 }
559
560 virtual void IMSHit(pkgAcquire::ItemDesc &item) {
dc5812ec
JF
561 }
562
563 virtual void Fetch(pkgAcquire::ItemDesc &item) {
36bb2ca2 564 [delegate_ setProgressTitle:[NSString stringWithCString:("Downloading " + item.ShortDesc).c_str()]];
dc5812ec
JF
565 }
566
567 virtual void Done(pkgAcquire::ItemDesc &item) {
dc5812ec
JF
568 }
569
570 virtual void Fail(pkgAcquire::ItemDesc &item) {
1d80f6b9
JF
571 if (
572 item.Owner->Status == pkgAcquire::Item::StatIdle ||
573 item.Owner->Status == pkgAcquire::Item::StatDone
574 )
575 return;
576
36bb2ca2 577 [delegate_ setProgressError:[NSString stringWithCString:item.Owner->ErrorText.c_str()]];
dc5812ec
JF
578 }
579
580 virtual bool Pulse(pkgAcquire *Owner) {
2367a917
JF
581 bool value = pkgAcquireStatus::Pulse(Owner);
582
583 float percent(
584 double(CurrentBytes + CurrentItems) /
585 double(TotalBytes + TotalItems)
586 );
587
36bb2ca2 588 [delegate_ setProgressPercent:percent];
2367a917 589 return value;
dc5812ec
JF
590 }
591
592 virtual void Start() {
dc5812ec
JF
593 }
594
595 virtual void Stop() {
dc5812ec
JF
596 }
597};
598/* }}} */
599/* Progress Delegation {{{ */
600class Progress :
601 public OpProgress
602{
603 private:
36bb2ca2 604 _transient id<ProgressDelegate> delegate_;
dc5812ec
JF
605
606 protected:
607 virtual void Update() {
36bb2ca2
JF
608 [delegate_ setProgressTitle:[NSString stringWithCString:Op.c_str()]];
609 [delegate_ setProgressPercent:(Percent / 100)];
dc5812ec
JF
610 }
611
612 public:
613 Progress() :
614 delegate_(nil)
615 {
616 }
617
618 void setDelegate(id delegate) {
619 delegate_ = delegate;
620 }
621
622 virtual void Done() {
36bb2ca2 623 [delegate_ setProgressPercent:1];
dc5812ec
JF
624 }
625};
626/* }}} */
627
36bb2ca2
JF
628/* Database Interface {{{ */
629@interface Database : NSObject {
630 pkgCacheFile cache_;
631 pkgRecords *records_;
632 pkgProblemResolver *resolver_;
633 pkgAcquire *fetcher_;
634 FileFd *lock_;
635 SPtr<pkgPackageManager> manager_;
636 pkgSourceList *list_;
5f6bff8c 637
36bb2ca2
JF
638 NSMutableDictionary *sources_;
639 NSMutableArray *packages_;
b4d89997 640
36bb2ca2
JF
641 _transient id delegate_;
642 Status status_;
643 Progress progress_;
644 int statusfd_;
dc5812ec
JF
645}
646
36bb2ca2
JF
647- (void) _readStatus:(NSNumber *)fd;
648- (void) _readOutput:(NSNumber *)fd;
2367a917 649
36bb2ca2 650- (Package *) packageWithName:(NSString *)name;
dc5812ec 651
36bb2ca2
JF
652- (Database *) init;
653- (pkgCacheFile &) cache;
654- (pkgRecords *) records;
655- (pkgProblemResolver *) resolver;
656- (pkgAcquire &) fetcher;
657- (NSArray *) packages;
658- (void) reloadData;
659
660- (void) prepare;
661- (void) perform;
662- (void) upgrade;
663- (void) update;
664
665- (void) updateWithStatus:(Status &)status;
666
667- (void) setDelegate:(id)delegate;
668- (Source *) getSource:(const pkgCache::PkgFileIterator &)file;
dc5812ec 669@end
36bb2ca2 670/* }}} */
dc5812ec 671
36bb2ca2
JF
672/* Source Class {{{ */
673@interface Source : NSObject {
674 NSString *description_;
675 NSString *label_;
676 NSString *origin_;
dc5812ec 677
36bb2ca2
JF
678 NSString *uri_;
679 NSString *distribution_;
680 NSString *type_;
681 NSString *version_;
2367a917 682
36bb2ca2 683 NSString *defaultIcon_;
dc5812ec 684
36bb2ca2 685 BOOL trusted_;
dc5812ec
JF
686}
687
36bb2ca2 688- (Source *) initWithMetaIndex:(metaIndex *)index;
dc5812ec 689
36bb2ca2 690- (BOOL) trusted;
dc5812ec 691
36bb2ca2
JF
692- (NSString *) uri;
693- (NSString *) distribution;
694- (NSString *) type;
695
696- (NSString *) description;
697- (NSString *) label;
698- (NSString *) origin;
699- (NSString *) version;
dc5812ec 700
36bb2ca2 701- (NSString *) defaultIcon;
dc5812ec 702@end
dc5812ec 703
36bb2ca2 704@implementation Source
dc5812ec 705
36bb2ca2
JF
706- (void) dealloc {
707 [uri_ release];
708 [distribution_ release];
709 [type_ release];
dc5812ec 710
36bb2ca2
JF
711 if (description_ != nil)
712 [description_ release];
713 if (label_ != nil)
714 [label_ release];
715 if (origin_ != nil)
716 [origin_ release];
717 if (version_ != nil)
718 [version_ release];
719 if (defaultIcon_ != nil)
720 [defaultIcon_ release];
dc5812ec 721
36bb2ca2 722 [super dealloc];
dc5812ec
JF
723}
724
36bb2ca2
JF
725- (Source *) initWithMetaIndex:(metaIndex *)index {
726 if ((self = [super init]) != nil) {
727 trusted_ = index->IsTrusted();
dc5812ec 728
36bb2ca2
JF
729 uri_ = [[NSString stringWithCString:index->GetURI().c_str()] retain];
730 distribution_ = [[NSString stringWithCString:index->GetDist().c_str()] retain];
731 type_ = [[NSString stringWithCString:index->GetType()] retain];
dc5812ec 732
36bb2ca2
JF
733 description_ = nil;
734 label_ = nil;
735 origin_ = nil;
736 version_ = nil;
737 defaultIcon_ = nil;
dc5812ec 738
36bb2ca2
JF
739 debReleaseIndex *dindex(dynamic_cast<debReleaseIndex *>(index));
740 if (dindex != NULL) {
741 std::ifstream release(dindex->MetaIndexFile("Release").c_str());
742 std::string line;
743 while (std::getline(release, line)) {
744 std::string::size_type colon(line.find(':'));
745 if (colon == std::string::npos)
746 continue;
dc5812ec 747
36bb2ca2
JF
748 std::string name(line.substr(0, colon));
749 std::string value(line.substr(colon + 1));
750 while (!value.empty() && value[0] == ' ')
751 value = value.substr(1);
dc5812ec 752
36bb2ca2
JF
753 if (name == "Default-Icon")
754 defaultIcon_ = [[NSString stringWithCString:value.c_str()] retain];
755 else if (name == "Description")
756 description_ = [[NSString stringWithCString:value.c_str()] retain];
757 else if (name == "Label")
758 label_ = [[NSString stringWithCString:value.c_str()] retain];
759 else if (name == "Origin")
760 origin_ = [[NSString stringWithCString:value.c_str()] retain];
761 else if (name == "Version")
762 version_ = [[NSString stringWithCString:value.c_str()] retain];
763 }
764 }
765 } return self;
2367a917 766}
dc5812ec 767
36bb2ca2
JF
768- (BOOL) trusted {
769 return trusted_;
770}
3178d79b 771
36bb2ca2
JF
772- (NSString *) uri {
773 return uri_;
774}
ec97ef06 775
36bb2ca2
JF
776- (NSString *) distribution {
777 return distribution_;
778}
dc5812ec 779
36bb2ca2
JF
780- (NSString *) type {
781 return type_;
dc5812ec
JF
782}
783
36bb2ca2
JF
784- (NSString *) description {
785 return description_;
786}
b4d89997 787
36bb2ca2
JF
788- (NSString *) label {
789 return label_;
790}
3178d79b 791
36bb2ca2
JF
792- (NSString *) origin {
793 return origin_;
794}
3178d79b 795
36bb2ca2
JF
796- (NSString *) version {
797 return version_;
798}
2367a917 799
36bb2ca2
JF
800- (NSString *) defaultIcon {
801 return defaultIcon_;
802}
2367a917 803
dc5812ec 804@end
b4d89997 805/* }}} */
36bb2ca2
JF
806/* Package Class {{{ */
807NSString *Scour(const char *field, const char *begin, const char *end) {
808 size_t i(0), l(strlen(field));
dc5812ec 809
36bb2ca2
JF
810 for (;;) {
811 const char *name = begin + i;
812 const char *colon = name + l;
813 const char *value = colon + 1;
06aa974d 814
36bb2ca2
JF
815 if (
816 value < end &&
817 *colon == ':' &&
818 memcmp(name, field, l) == 0
819 ) {
820 while (value != end && value[0] == ' ')
821 ++value;
822 const char *line = std::find(value, end, '\n');
823 while (line != value && line[-1] == ' ')
824 --line;
825 return [NSString stringWithCString:value length:(line - value)];
826 } else {
827 begin = std::find(begin, end, '\n');
828 if (begin == end)
829 return nil;
830 ++begin;
831 }
832 }
833}
b4d89997 834
36bb2ca2
JF
835@interface Package : NSObject {
836 pkgCache::PkgIterator iterator_;
837 _transient Database *database_;
838 pkgCache::VerIterator version_;
839 pkgCache::VerFileIterator file_;
840 Source *source_;
dc5812ec 841
36bb2ca2
JF
842 NSString *latest_;
843 NSString *installed_;
dc5812ec 844
36bb2ca2
JF
845 NSString *id_;
846 NSString *name_;
847 NSString *tagline_;
848 NSString *icon_;
849 NSString *website_;
b4d89997
JF
850}
851
36bb2ca2
JF
852- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file;
853+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database;
b4d89997 854
36bb2ca2
JF
855- (NSString *) section;
856- (Address *) maintainer;
857- (size_t) size;
858- (NSString *) description;
859- (NSString *) index;
dc5812ec 860
36bb2ca2 861- (NSDate *) seen;
9e98e020 862
36bb2ca2
JF
863- (NSString *) latest;
864- (NSString *) installed;
865- (BOOL) upgradable;
866- (BOOL) essential;
867- (BOOL) broken;
b4d89997 868
36bb2ca2
JF
869- (NSString *) id;
870- (NSString *) name;
871- (NSString *) tagline;
872- (NSString *) icon;
873- (NSString *) website;
b4d89997 874
36bb2ca2 875- (Source *) source;
b4d89997 876
36bb2ca2 877- (BOOL) matches:(NSString *)text;
b4d89997 878
36bb2ca2
JF
879- (NSComparisonResult) compareByName:(Package *)package;
880- (NSComparisonResult) compareBySection:(Package *)package;
881- (NSComparisonResult) compareBySectionAndName:(Package *)package;
882- (NSComparisonResult) compareForChanges:(Package *)package;
b4d89997 883
36bb2ca2
JF
884- (void) install;
885- (void) remove;
06aa974d 886
36bb2ca2
JF
887- (NSNumber *) isSearchedForBy:(NSString *)search;
888- (NSNumber *) isInstalledInSection:(NSString *)section;
889- (NSNumber *) isUninstalledInSection:(NSString *)section;
b4d89997 890
36bb2ca2 891@end
b4d89997 892
36bb2ca2 893@implementation Package
b4d89997 894
36bb2ca2
JF
895- (void) dealloc {
896 [latest_ release];
897 if (installed_ != nil)
898 [installed_ release];
b4d89997 899
36bb2ca2
JF
900 [id_ release];
901 if (name_ != nil)
902 [name_ release];
903 [tagline_ release];
904 if (icon_ != nil)
905 [icon_ release];
906 if (website_ != nil)
907 [website_ release];
9e98e020 908
36bb2ca2 909 [source_ release];
06aa974d 910
36bb2ca2 911 [super dealloc];
b4d89997
JF
912}
913
36bb2ca2
JF
914- (Package *) initWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database version:(pkgCache::VerIterator)version file:(pkgCache::VerFileIterator)file {
915 if ((self = [super init]) != nil) {
916 iterator_ = iterator;
917 database_ = database;
b4d89997 918
36bb2ca2
JF
919 version_ = version;
920 file_ = file;
b4d89997 921
36bb2ca2 922 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
b4d89997 923
36bb2ca2
JF
924 const char *begin, *end;
925 parser->GetRec(begin, end);
b4d89997 926
36bb2ca2
JF
927 latest_ = [[NSString stringWithCString:version_.VerStr()] retain];
928 installed_ = iterator_.CurrentVer().end() ? nil : [[NSString stringWithCString:iterator_.CurrentVer().VerStr()] retain];
2367a917 929
36bb2ca2
JF
930 id_ = [[[NSString stringWithCString:iterator_.Name()] lowercaseString] retain];
931 name_ = Scour("Name", begin, end);
932 if (name_ != nil)
933 name_ = [name_ retain];
934 tagline_ = [[NSString stringWithCString:parser->ShortDesc().c_str()] retain];
935 icon_ = Scour("Icon", begin, end);
936 if (icon_ != nil)
937 icon_ = [icon_ retain];
938 website_ = Scour("Website", begin, end);
939 if (website_ != nil)
940 website_ = [website_ retain];
06aa974d 941
36bb2ca2 942 source_ = [[database_ getSource:file_.File()] retain];
06aa974d 943
36bb2ca2
JF
944 NSMutableDictionary *metadata = [Packages_ objectForKey:id_];
945 if (metadata == nil || [metadata count] == 0) {
946 metadata = [NSMutableDictionary dictionaryWithObjectsAndKeys:
947 now_, @"FirstSeen",
948 nil];
b4d89997 949
36bb2ca2
JF
950 [Packages_ setObject:metadata forKey:id_];
951 }
952 } return self;
dc5812ec
JF
953}
954
36bb2ca2
JF
955+ (Package *) packageWithIterator:(pkgCache::PkgIterator)iterator database:(Database *)database {
956 for (pkgCache::VerIterator version = iterator.VersionList(); !version.end(); ++version)
957 for (pkgCache::VerFileIterator file = version.FileList(); !file.end(); ++file)
958 return [[[Package alloc]
959 initWithIterator:iterator
960 database:database
961 version:version
962 file:file]
963 autorelease];
964 return nil;
dc5812ec
JF
965}
966
36bb2ca2
JF
967- (NSString *) section {
968 const char *section = iterator_.Section();
969 return section == NULL ? nil : [[NSString stringWithCString:section] stringByReplacingCharacter:'_' withCharacter:' '];
dc5812ec
JF
970}
971
36bb2ca2
JF
972- (Address *) maintainer {
973 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
974 return [Address addressWithString:[NSString stringWithCString:parser->Maintainer().c_str()]];
8fe19fc1
JF
975}
976
36bb2ca2
JF
977- (size_t) size {
978 return version_->InstalledSize;
dc5812ec
JF
979}
980
36bb2ca2
JF
981- (NSString *) description {
982 pkgRecords::Parser *parser = &[database_ records]->Lookup(file_);
983 NSString *description([NSString stringWithCString:parser->LongDesc().c_str()]);
8fe19fc1 984
36bb2ca2
JF
985 NSArray *lines = [description componentsSeparatedByString:@"\n"];
986 NSMutableArray *trimmed = [NSMutableArray arrayWithCapacity:([lines count] - 1)];
987 if ([lines count] < 2)
988 return nil;
3178d79b 989
36bb2ca2
JF
990 NSCharacterSet *whitespace = [NSCharacterSet whitespaceCharacterSet];
991 for (size_t i(1); i != [lines count]; ++i) {
992 NSString *trim = [[lines objectAtIndex:i] stringByTrimmingCharactersInSet:whitespace];
993 [trimmed addObject:trim];
994 }
3178d79b 995
36bb2ca2
JF
996 return [trimmed componentsJoinedByString:@"\n"];
997}
dc5812ec 998
36bb2ca2
JF
999- (NSString *) index {
1000 NSString *index = [[[self name] substringToIndex:1] uppercaseString];
1001 return [index length] != 0 && isalpha([index characterAtIndex:0]) ? index : @"123";
1002}
3178d79b 1003
36bb2ca2
JF
1004- (NSDate *) seen {
1005 return [[Packages_ objectForKey:id_] objectForKey:@"FirstSeen"];
3178d79b
JF
1006}
1007
36bb2ca2
JF
1008- (NSString *) latest {
1009 return latest_;
8fe19fc1
JF
1010}
1011
36bb2ca2
JF
1012- (NSString *) installed {
1013 return installed_;
3178d79b
JF
1014}
1015
36bb2ca2
JF
1016- (BOOL) upgradable {
1017 if (NSString *installed = [self installed])
1018 return [[self latest] compare:installed] != NSOrderedSame ? YES : NO;
1019 else
1020 return [self essential];
1021}
3178d79b 1022
36bb2ca2
JF
1023- (BOOL) essential {
1024 return (iterator_->Flags & pkgCache::Flag::Essential) == 0 ? NO : YES;
3178d79b
JF
1025}
1026
36bb2ca2
JF
1027- (BOOL) broken {
1028 return (*[database_ cache])[iterator_].InstBroken();
8fe19fc1
JF
1029}
1030
36bb2ca2
JF
1031- (NSString *) id {
1032 return id_;
8fe19fc1
JF
1033}
1034
36bb2ca2
JF
1035- (NSString *) name {
1036 return name_ == nil ? id_ : name_;
1037}
8fe19fc1 1038
36bb2ca2
JF
1039- (NSString *) tagline {
1040 return tagline_;
8fe19fc1
JF
1041}
1042
36bb2ca2
JF
1043- (NSString *) icon {
1044 return icon_;
1045}
8fe19fc1 1046
36bb2ca2
JF
1047- (NSString *) website {
1048 return website_;
8fe19fc1
JF
1049}
1050
36bb2ca2
JF
1051- (Source *) source {
1052 return source_;
8fe19fc1
JF
1053}
1054
36bb2ca2
JF
1055- (BOOL) matches:(NSString *)text {
1056 if (text == nil)
1057 return NO;
8fe19fc1 1058
36bb2ca2 1059 NSRange range;
8fe19fc1 1060
36bb2ca2
JF
1061 range = [[self id] rangeOfString:text options:NSCaseInsensitiveSearch];
1062 if (range.location != NSNotFound)
1063 return YES;
8fe19fc1 1064
36bb2ca2
JF
1065 range = [[self name] rangeOfString:text options:NSCaseInsensitiveSearch];
1066 if (range.location != NSNotFound)
1067 return YES;
8fe19fc1 1068
36bb2ca2
JF
1069 range = [[self tagline] rangeOfString:text options:NSCaseInsensitiveSearch];
1070 if (range.location != NSNotFound)
1071 return YES;
8fe19fc1 1072
36bb2ca2
JF
1073 return NO;
1074}
8fe19fc1 1075
36bb2ca2
JF
1076- (NSComparisonResult) compareByName:(Package *)package {
1077 NSString *lhs = [self name];
1078 NSString *rhs = [package name];
8fe19fc1 1079
36bb2ca2
JF
1080 if ([lhs length] != 0 && [rhs length] != 0) {
1081 unichar lhc = [lhs characterAtIndex:0];
1082 unichar rhc = [rhs characterAtIndex:0];
1083
1084 if (isalpha(lhc) && !isalpha(rhc))
1085 return NSOrderedAscending;
1086 else if (!isalpha(lhc) && isalpha(rhc))
1087 return NSOrderedDescending;
8fe19fc1
JF
1088 }
1089
36bb2ca2 1090 return [lhs caseInsensitiveCompare:rhs];
8fe19fc1
JF
1091}
1092
36bb2ca2
JF
1093- (NSComparisonResult) compareBySection:(Package *)package {
1094 NSString *lhs = [self section];
1095 NSString *rhs = [package section];
3178d79b 1096
36bb2ca2
JF
1097 if (lhs == NULL && rhs != NULL)
1098 return NSOrderedAscending;
1099 else if (lhs != NULL && rhs == NULL)
1100 return NSOrderedDescending;
1101 else if (lhs != NULL && rhs != NULL) {
a72074b2 1102 NSComparisonResult result = [lhs caseInsensitiveCompare:rhs];
36bb2ca2
JF
1103 if (result != NSOrderedSame)
1104 return result;
1105 }
3178d79b 1106
36bb2ca2
JF
1107 return NSOrderedSame;
1108}
3178d79b 1109
36bb2ca2
JF
1110- (NSComparisonResult) compareBySectionAndName:(Package *)package {
1111 NSString *lhs = [self section];
1112 NSString *rhs = [package section];
3178d79b 1113
36bb2ca2
JF
1114 if (lhs == NULL && rhs != NULL)
1115 return NSOrderedAscending;
1116 else if (lhs != NULL && rhs == NULL)
1117 return NSOrderedDescending;
1118 else if (lhs != NULL && rhs != NULL) {
1119 NSComparisonResult result = [lhs compare:rhs];
1120 if (result != NSOrderedSame)
1121 return result;
1122 }
3178d79b 1123
36bb2ca2
JF
1124 return [self compareByName:package];
1125}
3178d79b 1126
36bb2ca2
JF
1127- (NSComparisonResult) compareForChanges:(Package *)package {
1128 BOOL lhs = [self upgradable];
1129 BOOL rhs = [package upgradable];
8fe19fc1 1130
36bb2ca2
JF
1131 if (lhs != rhs)
1132 return lhs ? NSOrderedAscending : NSOrderedDescending;
1133 else if (!lhs) {
1134 switch ([[self seen] compare:[package seen]]) {
1135 case NSOrderedAscending:
1136 return NSOrderedDescending;
1137 case NSOrderedSame:
1138 break;
1139 case NSOrderedDescending:
1140 return NSOrderedAscending;
1141 default:
1142 _assert(false);
1143 }
1144 }
8fe19fc1 1145
36bb2ca2
JF
1146 return [self compareByName:package];
1147}
8fe19fc1 1148
36bb2ca2
JF
1149- (void) install {
1150 pkgProblemResolver *resolver = [database_ resolver];
1151 resolver->Clear(iterator_);
1152 resolver->Protect(iterator_);
1153 pkgCacheFile &cache([database_ cache]);
1154 cache->MarkInstall(iterator_, false);
1155 pkgDepCache::StateCache &state((*cache)[iterator_]);
1156 if (!state.Install())
1157 cache->SetReInstall(iterator_, true);
1158}
68a238ec 1159
36bb2ca2
JF
1160- (void) remove {
1161 pkgProblemResolver *resolver = [database_ resolver];
1162 resolver->Clear(iterator_);
1163 resolver->Protect(iterator_);
1164 resolver->Remove(iterator_);
1165 [database_ cache]->MarkDelete(iterator_, true);
1166}
8fe19fc1 1167
36bb2ca2
JF
1168- (NSNumber *) isSearchedForBy:(NSString *)search {
1169 return [NSNumber numberWithBool:[self matches:search]];
1170}
8fe19fc1 1171
36bb2ca2
JF
1172- (NSNumber *) isInstalledInSection:(NSString *)section {
1173 return [NSNumber numberWithBool:([self installed] != nil && (section == nil || [section isEqualToString:[self section]]))];
1174}
8fe19fc1 1175
36bb2ca2
JF
1176- (NSNumber *) isUninstalledInSection:(NSString *)section {
1177 return [NSNumber numberWithBool:([self installed] == nil && (section == nil || [section isEqualToString:[self section]]))];
1178}
8fe19fc1 1179
36bb2ca2
JF
1180@end
1181/* }}} */
1182/* Section Class {{{ */
1183@interface Section : NSObject {
1184 NSString *name_;
1185 size_t row_;
1186 size_t count_;
1187}
3178d79b 1188
36bb2ca2
JF
1189- (Section *) initWithName:(NSString *)name row:(size_t)row;
1190- (NSString *) name;
1191- (size_t) row;
1192- (size_t) count;
1193- (void) addToCount;
b19871dd 1194
36bb2ca2 1195@end
b19871dd 1196
36bb2ca2 1197@implementation Section
b19871dd 1198
36bb2ca2
JF
1199- (void) dealloc {
1200 [name_ release];
1201 [super dealloc];
1202}
b19871dd 1203
36bb2ca2
JF
1204- (Section *) initWithName:(NSString *)name row:(size_t)row {
1205 if ((self = [super init]) != nil) {
1206 name_ = [name retain];
1207 row_ = row;
b19871dd
JF
1208 } return self;
1209}
1210
36bb2ca2
JF
1211- (NSString *) name {
1212 return name_;
1213}
1214
1215- (size_t) row {
1216 return row_;
1217}
1218
1219- (size_t) count {
1220 return count_;
1221}
1222
1223- (void) addToCount {
1224 ++count_;
1225}
1226
b19871dd
JF
1227@end
1228/* }}} */
1229
36bb2ca2
JF
1230/* Database Implementation {{{ */
1231@implementation Database
ec97ef06 1232
36bb2ca2
JF
1233- (void) dealloc {
1234 _assert(false);
1235 [super dealloc];
1236}
ec97ef06 1237
36bb2ca2
JF
1238- (void) _readStatus:(NSNumber *)fd {
1239 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
c7c6384e 1240
36bb2ca2
JF
1241 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1242 std::istream is(&ib);
1243 std::string line;
ec97ef06 1244
36bb2ca2
JF
1245 const char *error;
1246 int offset;
1247 pcre *code = pcre_compile("^([^:]*):([^:]*):([^:]*):(.*)$", 0, &error, &offset, NULL);
ec97ef06 1248
36bb2ca2
JF
1249 pcre_extra *study = NULL;
1250 int capture;
1251 pcre_fullinfo(code, study, PCRE_INFO_CAPTURECOUNT, &capture);
1252 int matches[(capture + 1) * 3];
ec97ef06 1253
36bb2ca2
JF
1254 while (std::getline(is, line)) {
1255 const char *data(line.c_str());
ec97ef06 1256
36bb2ca2 1257 _assert(pcre_exec(code, study, data, line.size(), 0, 0, matches, sizeof(matches) / sizeof(matches[0])) >= 0);
ec97ef06 1258
36bb2ca2
JF
1259 std::istringstream buffer(line.substr(matches[6], matches[7] - matches[6]));
1260 float percent;
1261 buffer >> percent;
1262 [delegate_ setProgressPercent:(percent / 100)];
c7c6384e 1263
36bb2ca2
JF
1264 NSString *string = [NSString stringWithCString:(data + matches[8]) length:(matches[9] - matches[8])];
1265 std::string type(line.substr(matches[2], matches[3] - matches[2]));
ec97ef06 1266
36bb2ca2
JF
1267 if (type == "pmerror")
1268 [delegate_ setProgressError:string];
1269 else if (type == "pmstatus")
1270 [delegate_ setProgressTitle:string];
1271 else if (type == "pmconffile")
1272 ;
1273 else _assert(false);
1274 }
ec97ef06 1275
36bb2ca2
JF
1276 [pool release];
1277 _assert(false);
1278}
ec97ef06 1279
36bb2ca2
JF
1280- (void) _readOutput:(NSNumber *)fd {
1281 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ec97ef06 1282
36bb2ca2
JF
1283 __gnu_cxx::stdio_filebuf<char> ib([fd intValue], std::ios::in);
1284 std::istream is(&ib);
1285 std::string line;
1286
1287 while (std::getline(is, line))
1288 [delegate_ addProgressOutput:[NSString stringWithCString:line.c_str()]];
1289
1290 [pool release];
1291 _assert(false);
ec97ef06
JF
1292}
1293
36bb2ca2
JF
1294- (Package *) packageWithName:(NSString *)name {
1295 pkgCache::PkgIterator iterator(cache_->FindPkg([name UTF8String]));
1296 return iterator.end() ? nil : [Package packageWithIterator:iterator database:self];
1297}
1298
1299- (Database *) init {
ec97ef06 1300 if ((self = [super init]) != nil) {
36bb2ca2
JF
1301 records_ = NULL;
1302 resolver_ = NULL;
1303 fetcher_ = NULL;
1304 lock_ = NULL;
ec97ef06 1305
36bb2ca2
JF
1306 sources_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
1307 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
ec97ef06 1308
36bb2ca2 1309 int fds[2];
ec97ef06 1310
36bb2ca2
JF
1311 _assert(pipe(fds) != -1);
1312 statusfd_ = fds[1];
ec97ef06 1313
36bb2ca2
JF
1314 [NSThread
1315 detachNewThreadSelector:@selector(_readStatus:)
1316 toTarget:self
1317 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1318 ];
ec97ef06 1319
36bb2ca2
JF
1320 _assert(pipe(fds) != -1);
1321 _assert(dup2(fds[1], 1) != -1);
1322 _assert(close(fds[1]) != -1);
1323
1324 [NSThread
1325 detachNewThreadSelector:@selector(_readOutput:)
1326 toTarget:self
1327 withObject:[[NSNumber numberWithInt:fds[0]] retain]
1328 ];
ec97ef06
JF
1329 } return self;
1330}
1331
36bb2ca2
JF
1332- (pkgCacheFile &) cache {
1333 return cache_;
ec97ef06
JF
1334}
1335
36bb2ca2
JF
1336- (pkgRecords *) records {
1337 return records_;
ec97ef06
JF
1338}
1339
36bb2ca2
JF
1340- (pkgProblemResolver *) resolver {
1341 return resolver_;
ec97ef06
JF
1342}
1343
36bb2ca2
JF
1344- (pkgAcquire &) fetcher {
1345 return *fetcher_;
ec97ef06
JF
1346}
1347
36bb2ca2
JF
1348- (NSArray *) packages {
1349 return packages_;
ec97ef06
JF
1350}
1351
36bb2ca2
JF
1352- (void) reloadData {
1353 _error->Discard();
1354 delete list_;
1355 manager_ = NULL;
1356 delete lock_;
1357 delete fetcher_;
1358 delete resolver_;
1359 delete records_;
1360 cache_.Close();
1361
1362 if (!cache_.Open(progress_, true)) {
1363 fprintf(stderr, "repairing corrupted database...\n");
1364 _error->Discard();
1365 [self updateWithStatus:status_];
1366 _assert(cache_.Open(progress_, true));
1367 }
1368
1369 now_ = [[NSDate date] retain];
1370
1371 records_ = new pkgRecords(cache_);
1372 resolver_ = new pkgProblemResolver(cache_);
1373 fetcher_ = new pkgAcquire(&status_);
1374 lock_ = NULL;
1375
1376 list_ = new pkgSourceList();
1377 _assert(list_->ReadMainList());
1378
1379 [sources_ removeAllObjects];
1380 for (pkgSourceList::const_iterator source = list_->begin(); source != list_->end(); ++source) {
1381 std::vector<pkgIndexFile *> *indices = (*source)->GetIndexFiles();
1382 for (std::vector<pkgIndexFile *>::const_iterator index = indices->begin(); index != indices->end(); ++index)
1383 [sources_
1384 setObject:[[[Source alloc] initWithMetaIndex:*source] autorelease]
1385 forKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(*index)]
1386 ];
1387 }
1388
1389 [packages_ removeAllObjects];
1390 for (pkgCache::PkgIterator iterator = cache_->PkgBegin(); !iterator.end(); ++iterator)
1391 if (Package *package = [Package packageWithIterator:iterator database:self])
1392 if ([package source] != nil || [package installed] != nil)
1393 [packages_ addObject:package];
ec97ef06
JF
1394}
1395
36bb2ca2
JF
1396- (void) prepare {
1397 pkgRecords records(cache_);
1398
1399 lock_ = new FileFd();
1400 lock_->Fd(GetLock(_config->FindDir("Dir::Cache::Archives") + "lock"));
1401 _assert(!_error->PendingError());
1402
1403 pkgSourceList list;
1404 // XXX: explain this with an error message
1405 _assert(list.ReadMainList());
1406
1407 manager_ = (_system->CreatePM(cache_));
1408 _assert(manager_->GetArchives(fetcher_, &list, &records));
1409 _assert(!_error->PendingError());
ec97ef06
JF
1410}
1411
36bb2ca2 1412- (void) perform {
a72074b2
JF
1413 NSMutableArray *before = [NSMutableArray arrayWithCapacity:16]; {
1414 pkgSourceList list;
1415 _assert(list.ReadMainList());
1416 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1417 [before addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1418 }
a0be02eb 1419
36bb2ca2
JF
1420 if (fetcher_->Run(PulseInterval_) != pkgAcquire::Continue)
1421 return;
1422
1423 _system->UnLock();
1424 pkgPackageManager::OrderResult result = manager_->DoInstall(statusfd_);
1425
1426 if (result == pkgPackageManager::Failed)
1427 return;
1428 if (_error->PendingError())
1429 return;
1430 if (result != pkgPackageManager::Completed)
1431 return;
a0be02eb 1432
a72074b2
JF
1433 NSMutableArray *after = [NSMutableArray arrayWithCapacity:16]; {
1434 pkgSourceList list;
1435 _assert(list.ReadMainList());
1436 for (pkgSourceList::const_iterator source = list.begin(); source != list.end(); ++source)
1437 [after addObject:[NSString stringWithUTF8String:(*source)->GetURI().c_str()]];
1438 }
1439
1440 if (![before isEqualToArray:after])
1441 [self update];
ec97ef06
JF
1442}
1443
36bb2ca2
JF
1444- (void) upgrade {
1445 _assert(cache_->DelCount() == 0 && cache_->InstCount() == 0);
1446 _assert(pkgApplyStatus(cache_));
1447
1448 if (cache_->BrokenCount() != 0) {
1449 _assert(pkgFixBroken(cache_));
1450 _assert(cache_->BrokenCount() == 0);
1451 _assert(pkgMinimizeUpgrade(cache_));
1452 }
1453
1454 _assert(pkgDistUpgrade(cache_));
c7c6384e
JF
1455}
1456
36bb2ca2
JF
1457- (void) update {
1458 [self updateWithStatus:status_];
1459}
b4d89997 1460
36bb2ca2
JF
1461- (void) updateWithStatus:(Status &)status {
1462 pkgSourceList list;
1463 _assert(list.ReadMainList());
b4d89997 1464
36bb2ca2
JF
1465 FileFd lock;
1466 lock.Fd(GetLock(_config->FindDir("Dir::State::Lists") + "lock"));
1467 _assert(!_error->PendingError());
1468
1469 pkgAcquire fetcher(&status);
1470 _assert(list.GetIndexes(&fetcher));
1471
1472 if (fetcher.Run(PulseInterval_) != pkgAcquire::Failed) {
1473 bool failed = false;
1474 for (pkgAcquire::ItemIterator item = fetcher.ItemsBegin(); item != fetcher.ItemsEnd(); item++)
1475 if ((*item)->Status != pkgAcquire::Item::StatDone) {
1476 (*item)->Finished();
1477 failed = true;
1478 }
1479
1480 if (!failed && _config->FindB("APT::Get::List-Cleanup", true) == true) {
1481 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists")));
1482 _assert(fetcher.Clean(_config->FindDir("Dir::State::lists") + "partial/"));
b4d89997 1483 }
36bb2ca2
JF
1484
1485 [Metadata_ setObject:[NSDate date] forKey:@"LastUpdate"];
b4d89997
JF
1486 }
1487}
1488
36bb2ca2
JF
1489- (void) setDelegate:(id)delegate {
1490 delegate_ = delegate;
1491 status_.setDelegate(delegate);
1492 progress_.setDelegate(delegate);
1493}
b4d89997 1494
36bb2ca2
JF
1495- (Source *) getSource:(const pkgCache::PkgFileIterator &)file {
1496 pkgIndexFile *index(NULL);
1497 list_->FindIndex(file, index);
1498 return [sources_ objectForKey:[NSNumber numberWithLong:reinterpret_cast<uintptr_t>(index)]];
b19871dd
JF
1499}
1500
36bb2ca2
JF
1501@end
1502/* }}} */
b4d89997 1503
36bb2ca2
JF
1504/* RVPage Interface {{{ */
1505@class RVBook;
b19871dd 1506
36bb2ca2
JF
1507@interface RVPage : UIView {
1508 _transient RVBook *book_;
1509 _transient id delegate_;
1510}
b4d89997 1511
36bb2ca2
JF
1512- (NSString *) title;
1513- (NSString *) backButtonTitle;
1514- (NSString *) rightButtonTitle;
1515- (NSString *) leftButtonTitle;
1516- (UIView *) accessoryView;
b4d89997 1517
36bb2ca2
JF
1518- (void) _rightButtonClicked;
1519- (void) _leftButtonClicked;
b4d89997 1520
36bb2ca2
JF
1521- (void) setPageActive:(BOOL)active;
1522- (void) resetViewAnimated:(BOOL)animated;
b4d89997 1523
36bb2ca2
JF
1524- (void) setTitle:(NSString *)title;
1525- (void) setBackButtonTitle:(NSString *)title;
ec97ef06 1526
36bb2ca2
JF
1527- (void) reloadButtons;
1528- (void) reloadData;
b4d89997 1529
36bb2ca2 1530- (id) initWithBook:(RVBook *)book;
b19871dd 1531
36bb2ca2 1532- (void) setDelegate:(id)delegate;
b19871dd 1533
36bb2ca2
JF
1534@end
1535/* }}} */
1536/* Reset View {{{ */
1537@protocol RVDelegate
1538- (void) setPageActive:(BOOL)active with:(id)object;
1539- (void) resetViewAnimated:(BOOL)animated with:(id)object;
1540- (void) reloadDataWith:(id)object;
1541@end
b19871dd 1542
36bb2ca2
JF
1543@interface RVBook : UIView {
1544 NSMutableArray *pages_;
1545 UINavigationBar *navbar_;
1546 UITransitionView *transition_;
1547 BOOL resetting_;
1548 _transient id delegate_;
1549}
b4d89997 1550
36bb2ca2
JF
1551- (id) initWithFrame:(CGRect)frame;
1552- (void) setDelegate:(id)delegate;
ec97ef06 1553
36bb2ca2 1554- (void) setPage:(RVPage *)page;
ec97ef06 1555
36bb2ca2
JF
1556- (void) pushPage:(RVPage *)page;
1557- (void) popPages:(unsigned)pages;
b4d89997 1558
36bb2ca2 1559- (void) setPrompt:(NSString *)prompt;
b19871dd 1560
36bb2ca2
JF
1561- (void) resetViewAnimated:(BOOL)animated;
1562- (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page;
b4d89997 1563
36bb2ca2
JF
1564- (void) setTitle:(NSString *)title forPage:(RVPage *)page;
1565- (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page;
1566- (void) reloadButtonsForPage:(RVPage *)page;
b4d89997 1567
36bb2ca2 1568- (void) reloadData;
b4d89997 1569
36bb2ca2 1570- (CGRect) pageBounds;
b4d89997 1571
36bb2ca2 1572@end
b4d89997 1573
36bb2ca2 1574@implementation RVBook
ec97ef06 1575
36bb2ca2
JF
1576- (void) dealloc {
1577 [navbar_ setDelegate:nil];
b4d89997 1578
36bb2ca2
JF
1579 [pages_ release];
1580 [navbar_ release];
1581 [transition_ release];
1582 [super dealloc];
b19871dd
JF
1583}
1584
36bb2ca2
JF
1585- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1586 _assert([pages_ count] != 0);
1587 RVPage *page = [pages_ lastObject];
1588 switch (button) {
1589 case 0: [page _rightButtonClicked]; break;
1590 case 1: [page _leftButtonClicked]; break;
1591 }
b19871dd
JF
1592}
1593
36bb2ca2
JF
1594- (void) navigationBar:(UINavigationBar *)navbar poppedItem:(UINavigationItem *)item {
1595 _assert([pages_ count] != 0);
1596 if (!resetting_)
1597 [[pages_ lastObject] setPageActive:NO];
1598 [pages_ removeLastObject];
1599 if (!resetting_)
1600 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
b19871dd
JF
1601}
1602
36bb2ca2
JF
1603- (id) initWithFrame:(CGRect)frame {
1604 if ((self = [super initWithFrame:frame]) != nil) {
1605 pages_ = [[NSMutableArray arrayWithCapacity:4] retain];
b4d89997 1606
36bb2ca2
JF
1607 struct CGRect bounds = [self bounds];
1608 CGSize navsize = [UINavigationBar defaultSizeWithPrompt];
1609 CGRect navrect = {{0, 0}, navsize};
b4d89997 1610
36bb2ca2
JF
1611 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1612 [self addSubview:navbar_];
b4d89997 1613
36bb2ca2
JF
1614 [navbar_ setBarStyle:1];
1615 [navbar_ setDelegate:self];
b4d89997 1616
36bb2ca2 1617 [navbar_ setPrompt:@""];
b4d89997 1618
36bb2ca2
JF
1619 transition_ = [[UITransitionView alloc] initWithFrame:CGRectMake(
1620 bounds.origin.x, bounds.origin.y + navsize.height, bounds.size.width, bounds.size.height - navsize.height
1621 )];
b4d89997 1622
36bb2ca2
JF
1623 [self addSubview:transition_];
1624 } return self;
b4d89997
JF
1625}
1626
36bb2ca2
JF
1627- (void) setDelegate:(id)delegate {
1628 delegate_ = delegate;
b19871dd
JF
1629}
1630
36bb2ca2
JF
1631- (void) setPage:(RVPage *)page {
1632 if ([pages_ count] != 0)
1633 [[pages_ lastObject] setPageActive:NO];
b19871dd 1634
36bb2ca2
JF
1635 [navbar_ disableAnimation];
1636 resetting_ = true;
1637 for (unsigned i(0), pages([pages_ count]); i != pages; ++i)
1638 [navbar_ popNavigationItem];
1639 resetting_ = false;
b19871dd 1640
36bb2ca2
JF
1641 [self pushPage:page];
1642 [navbar_ enableAnimation];
49048579
JF
1643}
1644
36bb2ca2
JF
1645- (void) pushPage:(RVPage *)page {
1646 if ([pages_ count] != 0)
1647 [[pages_ lastObject] setPageActive:NO];
49048579 1648
4cf4165e 1649 NSString *title = Simplify([page title]);
b4d89997 1650
36bb2ca2
JF
1651 NSString *backButtonTitle = [page backButtonTitle];
1652 if (backButtonTitle == nil)
1653 backButtonTitle = title;
b19871dd 1654
36bb2ca2
JF
1655 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
1656 [navitem setBackButtonTitle:backButtonTitle];
1657 [navbar_ pushNavigationItem:navitem];
b19871dd 1658
36bb2ca2
JF
1659 BOOL animated = [pages_ count] == 0 ? NO : YES;
1660 [transition_ transition:(animated ? 1 : 0) toView:page];
1661 [page setPageActive:YES];
b4d89997 1662
36bb2ca2
JF
1663 [pages_ addObject:page];
1664 [self reloadButtonsForPage:page];
23672b39 1665
36bb2ca2 1666 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:NO];
ec97ef06
JF
1667}
1668
36bb2ca2
JF
1669- (void) popPages:(unsigned)pages {
1670 if (pages == 0)
1671 return;
b4d89997 1672
36bb2ca2 1673 [[pages_ lastObject] setPageActive:NO];
2a8d9add 1674
36bb2ca2
JF
1675 resetting_ = true;
1676 for (unsigned i(0); i != pages; ++i)
1677 [navbar_ popNavigationItem];
1678 resetting_ = false;
b4d89997 1679
36bb2ca2
JF
1680 [self resetViewAnimated:YES toPage:[pages_ lastObject]];
1681}
b4d89997 1682
36bb2ca2
JF
1683- (void) setPrompt:(NSString *)prompt {
1684 [navbar_ setPrompt:prompt];
b4d89997
JF
1685}
1686
36bb2ca2
JF
1687- (void) resetViewAnimated:(BOOL)animated {
1688 resetting_ = true;
1689
1690 if ([pages_ count] > 1) {
1691 [navbar_ disableAnimation];
1692 while ([pages_ count] != (animated ? 2 : 1))
1693 [navbar_ popNavigationItem];
1694 [navbar_ enableAnimation];
1695 if (animated)
1696 [navbar_ popNavigationItem];
1697 }
1698
1699 resetting_ = false;
1700
1701 [self resetViewAnimated:animated toPage:[pages_ lastObject]];
1702}
2a8d9add 1703
36bb2ca2
JF
1704- (void) resetViewAnimated:(BOOL)animated toPage:(RVPage *)page {
1705 [page resetViewAnimated:animated];
1706 [transition_ transition:(animated ? 2 : 0) toView:page];
1707 [page setPageActive:YES];
1708 [self reloadButtonsForPage:page];
1709 [navbar_ setAccessoryView:[page accessoryView] animate:animated goingBack:YES];
1710}
2a8d9add 1711
36bb2ca2
JF
1712- (void) setTitle:(NSString *)title forPage:(RVPage *)page {
1713 if ([pages_ count] == 0 || page != [pages_ lastObject])
1714 return;
1715 UINavigationItem *navitem = [navbar_ topItem];
1716 [navitem setTitle:title];
1717}
2a8d9add 1718
36bb2ca2
JF
1719- (void) setBackButtonTitle:(NSString *)title forPage:(RVPage *)page {
1720 if ([pages_ count] == 0 || page != [pages_ lastObject])
1721 return;
1722 UINavigationItem *navitem = [navbar_ topItem];
1723 [navitem setBackButtonTitle:title];
b19871dd
JF
1724}
1725
36bb2ca2
JF
1726- (void) reloadButtonsForPage:(RVPage *)page {
1727 if ([pages_ count] == 0 || page != [pages_ lastObject])
1728 return;
1729 NSString *leftButtonTitle([pages_ count] == 1 ? [page leftButtonTitle] : nil);
1730 [navbar_ showButtonsWithLeftTitle:leftButtonTitle rightTitle:[page rightButtonTitle]];
1731}
9b70dd6d 1732
36bb2ca2
JF
1733- (void) reloadData {
1734 for (int i(0), e([pages_ count]); i != e; ++i) {
1735 RVPage *page([pages_ objectAtIndex:(e - i - 1)]);
1736 [page reloadData];
9b70dd6d 1737 }
36bb2ca2 1738}
9b70dd6d 1739
36bb2ca2
JF
1740- (CGRect) pageBounds {
1741 return [transition_ bounds];
b4d89997
JF
1742}
1743
36bb2ca2
JF
1744@end
1745/* }}} */
1746/* RVPage Implementation {{{ */
1747@implementation RVPage
b4d89997 1748
36bb2ca2
JF
1749- (NSString *) title {
1750 [self doesNotRecognizeSelector:_cmd];
1751 return nil;
1752}
b4d89997 1753
36bb2ca2
JF
1754- (NSString *) backButtonTitle {
1755 return nil;
b19871dd
JF
1756}
1757
36bb2ca2
JF
1758- (NSString *) leftButtonTitle {
1759 return nil;
b19871dd
JF
1760}
1761
36bb2ca2
JF
1762- (NSString *) rightButtonTitle {
1763 return nil;
b19871dd
JF
1764}
1765
36bb2ca2
JF
1766- (void) _rightButtonClicked {
1767 [self doesNotRecognizeSelector:_cmd];
b19871dd
JF
1768}
1769
36bb2ca2
JF
1770- (void) _leftButtonClicked {
1771 [self doesNotRecognizeSelector:_cmd];
1772}
b19871dd 1773
36bb2ca2
JF
1774- (UIView *) accessoryView {
1775 return nil;
1776}
b19871dd 1777
36bb2ca2
JF
1778- (void) setPageActive:(BOOL)active {
1779}
b19871dd 1780
36bb2ca2
JF
1781- (void) resetViewAnimated:(BOOL)animated {
1782 [self doesNotRecognizeSelector:_cmd];
b19871dd 1783}
3178d79b 1784
36bb2ca2
JF
1785- (void) setTitle:(NSString *)title {
1786 [book_ setTitle:title forPage:self];
b19871dd 1787}
3178d79b 1788
36bb2ca2
JF
1789- (void) setBackButtonTitle:(NSString *)title {
1790 [book_ setBackButtonTitle:title forPage:self];
b19871dd 1791}
3178d79b 1792
36bb2ca2
JF
1793- (void) reloadButtons {
1794 [book_ reloadButtonsForPage:self];
b19871dd 1795}
3178d79b 1796
36bb2ca2 1797- (void) reloadData {
b4d89997
JF
1798}
1799
36bb2ca2
JF
1800- (id) initWithBook:(RVBook *)book {
1801 if ((self = [super initWithFrame:[book pageBounds]]) != nil) {
1802 book_ = book;
1803 } return self;
b4d89997
JF
1804}
1805
36bb2ca2
JF
1806- (void) setDelegate:(id)delegate {
1807 delegate_ = delegate;
3178d79b
JF
1808}
1809
dc5812ec
JF
1810@end
1811/* }}} */
b19871dd 1812
36bb2ca2
JF
1813/* Confirmation View {{{ */
1814void AddTextView(NSMutableDictionary *fields, NSMutableArray *packages, NSString *key) {
1815 if ([packages count] == 0)
1816 return;
b4d89997 1817
36bb2ca2
JF
1818 UITextView *text = GetTextView([packages count] == 0 ? @"n/a" : [packages componentsJoinedByString:@", "], 120, false);
1819 [fields setObject:text forKey:key];
1820
1821 CGColor blue(space_, 0, 0, 0.4, 1);
1822 [text setTextColor:blue];
dc5812ec
JF
1823}
1824
36bb2ca2
JF
1825@protocol ConfirmationViewDelegate
1826- (void) cancel;
1827- (void) confirm;
1828@end
2367a917 1829
36bb2ca2
JF
1830@interface ConfirmationView : UIView {
1831 Database *database_;
1832 id delegate_;
1833 UITransitionView *transition_;
1834 UIView *overlay_;
1835 UINavigationBar *navbar_;
1836 UIPreferencesTable *table_;
1837 NSMutableDictionary *fields_;
1838 UIAlertSheet *essential_;
1839}
dc5812ec 1840
36bb2ca2 1841- (void) cancel;
8da60fb7 1842
36bb2ca2 1843- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate;
b4d89997 1844
dc5812ec
JF
1845@end
1846
36bb2ca2 1847@implementation ConfirmationView
dc5812ec 1848
2367a917 1849- (void) dealloc {
36bb2ca2
JF
1850 [navbar_ setDelegate:nil];
1851 [transition_ setDelegate:nil];
1852 [table_ setDataSource:nil];
1853
1854 [transition_ release];
1855 [overlay_ release];
1856 [navbar_ release];
8da60fb7 1857 [table_ release];
36bb2ca2
JF
1858 [fields_ release];
1859 if (essential_ != nil)
1860 [essential_ release];
2367a917
JF
1861 [super dealloc];
1862}
1863
36bb2ca2
JF
1864- (void) cancel {
1865 [transition_ transition:7 toView:nil];
1866 [delegate_ cancel];
1867}
1868
1869- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
1870 if (from != nil && to == nil)
1871 [self removeFromSuperview];
1872}
1873
1874- (void) navigationBar:(UINavigationBar *)navbar buttonClicked:(int)button {
1875 switch (button) {
1876 case 0:
1877 if (essential_ != nil)
1878 [essential_ popupAlertAnimated:YES];
1879 else
1880 [delegate_ confirm];
1881 break;
1882
1883 case 1:
1884 [self cancel];
1885 break;
1886 }
1887}
1888
1889- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
1890 [essential_ dismiss];
1891 [self cancel];
1892}
1893
dc5812ec 1894- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
36bb2ca2 1895 return 2;
dc5812ec
JF
1896}
1897
1898- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
1899 switch (group) {
36bb2ca2
JF
1900 case 0: return @"Statistics";
1901 case 1: return @"Modifications";
dc5812ec
JF
1902
1903 default: _assert(false);
1904 }
1905}
1906
1907- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
1908 switch (group) {
36bb2ca2
JF
1909 case 0: return 3;
1910 case 1: return [fields_ count];
dc5812ec
JF
1911
1912 default: _assert(false);
1913 }
1914}
1915
36bb2ca2
JF
1916- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
1917 if (group != 1 || row == -1)
1918 return proposed;
1919 else {
1920 _assert(size_t(row) < [fields_ count]);
1921 return [[[fields_ allValues] objectAtIndex:row] visibleTextRect].size.height + TextViewOffset_;
1922 }
1923}
1924
dc5812ec 1925- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
b4d89997
JF
1926 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
1927 [cell setShowSelection:NO];
dc5812ec
JF
1928
1929 switch (group) {
1930 case 0: switch (row) {
36bb2ca2
JF
1931 case 0: {
1932 [cell setTitle:@"Downloading"];
1933 [cell setValue:SizeString([database_ fetcher].FetchNeeded())];
1934 } break;
1935
1936 case 1: {
1937 [cell setTitle:@"Resuming At"];
1938 [cell setValue:SizeString([database_ fetcher].PartialPresent())];
1939 } break;
1940
1941 case 2: {
1942 double size([database_ cache]->UsrSize());
1943
1944 if (size < 0) {
1945 [cell setTitle:@"Disk Freeing"];
1946 [cell setValue:SizeString(-size)];
1947 } else {
1948 [cell setTitle:@"Disk Using"];
1949 [cell setValue:SizeString(size)];
1950 }
1951 } break;
1952
1953 default: _assert(false);
1954 } break;
1955
1956 case 1:
1957 _assert(size_t(row) < [fields_ count]);
1958 [cell setTitle:[[fields_ allKeys] objectAtIndex:row]];
1959 [cell addSubview:[[fields_ allValues] objectAtIndex:row]];
1960 break;
1961
1962 default: _assert(false);
1963 }
1964
1965 return cell;
1966}
1967
1968- (id) initWithView:(UIView *)view database:(Database *)database delegate:(id)delegate {
1969 if ((self = [super initWithFrame:[view bounds]]) != nil) {
1970 database_ = database;
1971 delegate_ = delegate;
b4d89997 1972
36bb2ca2
JF
1973 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
1974 [self addSubview:transition_];
b4d89997 1975
36bb2ca2 1976 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
06aa974d 1977
36bb2ca2
JF
1978 CGSize navsize = [UINavigationBar defaultSize];
1979 CGRect navrect = {{0, 0}, navsize};
1980 CGRect bounds = [overlay_ bounds];
b4d89997 1981
36bb2ca2
JF
1982 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
1983 [navbar_ setBarStyle:1];
1984 [navbar_ setDelegate:self];
dc5812ec 1985
36bb2ca2
JF
1986 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:@"Confirm"] autorelease];
1987 [navbar_ pushNavigationItem:navitem];
1988 [navbar_ showButtonsWithLeftTitle:@"Cancel" rightTitle:@"Confirm"];
dc5812ec 1989
36bb2ca2 1990 fields_ = [[NSMutableDictionary dictionaryWithCapacity:16] retain];
dc5812ec 1991
36bb2ca2
JF
1992 NSMutableArray *installing = [NSMutableArray arrayWithCapacity:16];
1993 NSMutableArray *reinstalling = [NSMutableArray arrayWithCapacity:16];
1994 NSMutableArray *upgrading = [NSMutableArray arrayWithCapacity:16];
1995 NSMutableArray *downgrading = [NSMutableArray arrayWithCapacity:16];
1996 NSMutableArray *removing = [NSMutableArray arrayWithCapacity:16];
dc5812ec 1997
36bb2ca2 1998 bool remove(false);
dc5812ec 1999
36bb2ca2
JF
2000 pkgCacheFile &cache([database_ cache]);
2001 for (pkgCache::PkgIterator iterator = cache->PkgBegin(); !iterator.end(); ++iterator) {
2002 Package *package([Package packageWithIterator:iterator database:database_]);
2003 NSString *name([package name]);
2004 bool essential((iterator->Flags & pkgCache::Flag::Essential) != 0);
2005 pkgDepCache::StateCache &state(cache[iterator]);
dc5812ec 2006
36bb2ca2
JF
2007 if (state.NewInstall())
2008 [installing addObject:name];
2009 else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
2010 [reinstalling addObject:name];
2011 else if (state.Upgrade())
2012 [upgrading addObject:name];
2013 else if (state.Downgrade())
2014 [downgrading addObject:name];
2015 else if (state.Delete()) {
2016 if (essential)
2017 remove = true;
2018 [removing addObject:name];
2019 }
2020 }
ec97ef06 2021
36bb2ca2
JF
2022 if (!remove)
2023 essential_ = nil;
2024 else {
2025 essential_ = [[UIAlertSheet alloc]
2026 initWithTitle:@"Unable to Comply"
2027 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2028 defaultButtonIndex:0
2029 delegate:self
2030 context:self
2031 ];
ec97ef06 2032
36bb2ca2
JF
2033 [essential_ setBodyText:@"One or more of the packages you are about to remove are marked 'Essential' and cannot be removed by Cydia. Please use apt-get."];
2034 }
ec97ef06 2035
36bb2ca2
JF
2036 AddTextView(fields_, installing, @"Installing");
2037 AddTextView(fields_, reinstalling, @"Reinstalling");
2038 AddTextView(fields_, upgrading, @"Upgrading");
2039 AddTextView(fields_, downgrading, @"Downgrading");
2040 AddTextView(fields_, removing, @"Removing");
dc5812ec 2041
36bb2ca2
JF
2042 table_ = [[UIPreferencesTable alloc] initWithFrame:CGRectMake(
2043 0, navsize.height, bounds.size.width, bounds.size.height - navsize.height
2044 )];
dc5812ec 2045
36bb2ca2
JF
2046 [table_ setReusesTableCells:YES];
2047 [table_ setDataSource:self];
2048 [table_ reloadData];
dc5812ec 2049
36bb2ca2
JF
2050 [overlay_ addSubview:navbar_];
2051 [overlay_ addSubview:table_];
8da60fb7 2052
36bb2ca2 2053 [view addSubview:self];
99364ec4 2054
36bb2ca2 2055 [transition_ setDelegate:self];
8da60fb7 2056
36bb2ca2
JF
2057 UIView *blank = [[[UIView alloc] initWithFrame:[transition_ bounds]] autorelease];
2058 [transition_ transition:0 toView:blank];
2059 [transition_ transition:3 toView:overlay_];
2060 } return self;
b4d89997 2061}
8da60fb7 2062
36bb2ca2
JF
2063@end
2064/* }}} */
8da60fb7 2065
36bb2ca2
JF
2066/* Progress Data {{{ */
2067@interface ProgressData : NSObject {
2068 SEL selector_;
2069 id target_;
2070 id object_;
dc5812ec
JF
2071}
2072
36bb2ca2 2073- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object;
b4d89997 2074
36bb2ca2
JF
2075- (SEL) selector;
2076- (id) target;
2077- (id) object;
2078@end
b4d89997 2079
36bb2ca2 2080@implementation ProgressData
b4d89997 2081
36bb2ca2
JF
2082- (ProgressData *) initWithSelector:(SEL)selector target:(id)target object:(id)object {
2083 if ((self = [super init]) != nil) {
2084 selector_ = selector;
2085 target_ = target;
2086 object_ = object;
2087 } return self;
2088}
b4d89997 2089
36bb2ca2
JF
2090- (SEL) selector {
2091 return selector_;
2092}
b4d89997 2093
36bb2ca2
JF
2094- (id) target {
2095 return target_;
8da60fb7
JF
2096}
2097
36bb2ca2
JF
2098- (id) object {
2099 return object_;
dc5812ec
JF
2100}
2101
2367a917
JF
2102@end
2103/* }}} */
36bb2ca2
JF
2104/* Progress View {{{ */
2105@interface ProgressView : UIView <
2106 ProgressDelegate
2107> {
2108 UIView *view_;
2109 UIView *background_;
2110 UITransitionView *transition_;
2111 UIView *overlay_;
2112 UINavigationBar *navbar_;
2113 UIProgressBar *progress_;
2114 UITextView *output_;
2115 UITextLabel *status_;
2116 id delegate_;
2367a917
JF
2117}
2118
36bb2ca2 2119- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to;
2367a917 2120
36bb2ca2
JF
2121- (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate;
2122- (void) setContentView:(UIView *)view;
2123- (void) resetView;
2367a917 2124
36bb2ca2
JF
2125- (void) _retachThread;
2126- (void) _detachNewThreadData:(ProgressData *)data;
2127- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title;
2367a917
JF
2128
2129@end
2130
36bb2ca2
JF
2131@protocol ProgressViewDelegate
2132- (void) progressViewIsComplete:(ProgressView *)sender;
2133@end
2134
2135@implementation ProgressView
2367a917
JF
2136
2137- (void) dealloc {
36bb2ca2
JF
2138 [transition_ setDelegate:nil];
2139 [navbar_ setDelegate:nil];
2140
2141 [view_ release];
2142 if (background_ != nil)
2143 [background_ release];
2144 [transition_ release];
2145 [overlay_ release];
2146 [navbar_ release];
2147 [progress_ release];
2148 [output_ release];
2149 [status_ release];
2367a917
JF
2150 [super dealloc];
2151}
2152
36bb2ca2
JF
2153- (void) transitionViewDidComplete:(UITransitionView*)view fromView:(UIView*)from toView:(UIView*)to {
2154 if (bootstrap_ && from == overlay_ && to == view_)
2155 exit(0);
2156}
2367a917 2157
36bb2ca2
JF
2158- (ProgressView *) initWithFrame:(struct CGRect)frame delegate:(id)delegate {
2159 if ((self = [super initWithFrame:frame]) != nil) {
2160 delegate_ = delegate;
ec97ef06 2161
36bb2ca2
JF
2162 transition_ = [[UITransitionView alloc] initWithFrame:[self bounds]];
2163 [transition_ setDelegate:self];
2367a917 2164
36bb2ca2 2165 overlay_ = [[UIView alloc] initWithFrame:[transition_ bounds]];
ec97ef06 2166
36bb2ca2
JF
2167 if (bootstrap_)
2168 [overlay_ setBackgroundColor:Black_];
2169 else {
2170 background_ = [[UIView alloc] initWithFrame:[self bounds]];
2171 [background_ setBackgroundColor:Black_];
2172 [self addSubview:background_];
2173 }
ec97ef06 2174
36bb2ca2 2175 [self addSubview:transition_];
2367a917 2176
36bb2ca2
JF
2177 CGSize navsize = [UINavigationBar defaultSize];
2178 CGRect navrect = {{0, 0}, navsize};
2367a917 2179
36bb2ca2
JF
2180 navbar_ = [[UINavigationBar alloc] initWithFrame:navrect];
2181 [overlay_ addSubview:navbar_];
b4d89997 2182
36bb2ca2
JF
2183 [navbar_ setBarStyle:1];
2184 [navbar_ setDelegate:self];
2367a917 2185
36bb2ca2
JF
2186 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:nil] autorelease];
2187 [navbar_ pushNavigationItem:navitem];
c7c6384e 2188
36bb2ca2
JF
2189 CGRect bounds = [overlay_ bounds];
2190 CGSize prgsize = [UIProgressBar defaultSize];
ec97ef06 2191
36bb2ca2
JF
2192 CGRect prgrect = {{
2193 (bounds.size.width - prgsize.width) / 2,
2194 bounds.size.height - prgsize.height - 20
2195 }, prgsize};
ec97ef06 2196
36bb2ca2
JF
2197 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
2198 [overlay_ addSubview:progress_];
ec97ef06 2199
36bb2ca2
JF
2200 status_ = [[UITextLabel alloc] initWithFrame:CGRectMake(
2201 10,
2202 bounds.size.height - prgsize.height - 50,
2203 bounds.size.width - 20,
2204 24
2205 )];
ec97ef06 2206
36bb2ca2
JF
2207 [status_ setColor:White_];
2208 [status_ setBackgroundColor:Clear_];
ec97ef06 2209
36bb2ca2
JF
2210 [status_ setCentersHorizontally:YES];
2211 //[status_ setFont:font];
ec97ef06 2212
36bb2ca2
JF
2213 output_ = [[UITextView alloc] initWithFrame:CGRectMake(
2214 10,
2215 navrect.size.height + 20,
2216 bounds.size.width - 20,
2217 bounds.size.height - navsize.height - 62 - navrect.size.height
2218 )];
1d80f6b9 2219
36bb2ca2
JF
2220 //[output_ setTextFont:@"Courier New"];
2221 [output_ setTextSize:12];
2367a917 2222
36bb2ca2
JF
2223 [output_ setTextColor:White_];
2224 [output_ setBackgroundColor:Clear_];
2367a917 2225
36bb2ca2
JF
2226 [output_ setMarginTop:0];
2227 [output_ setAllowsRubberBanding:YES];
b4d89997 2228
36bb2ca2
JF
2229 [overlay_ addSubview:output_];
2230 [overlay_ addSubview:status_];
2367a917 2231
36bb2ca2
JF
2232 [progress_ setStyle:0];
2233 } return self;
2367a917
JF
2234}
2235
36bb2ca2
JF
2236- (void) setContentView:(UIView *)view {
2237 view_ = [view retain];
2367a917
JF
2238}
2239
36bb2ca2
JF
2240- (void) resetView {
2241 [transition_ transition:6 toView:view_];
2367a917
JF
2242}
2243
36bb2ca2
JF
2244- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2245 [sheet dismiss];
2367a917
JF
2246}
2247
36bb2ca2
JF
2248- (void) _retachThread {
2249 [delegate_ progressViewIsComplete:self];
2250 [self resetView];
b19871dd
JF
2251}
2252
36bb2ca2 2253- (void) _detachNewThreadData:(ProgressData *)data {
b4d89997
JF
2254 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
2255
36bb2ca2
JF
2256 [[data target] performSelector:[data selector] withObject:[data object]];
2257 [data release];
b19871dd 2258
36bb2ca2 2259 [self performSelectorOnMainThread:@selector(_retachThread) withObject:nil waitUntilDone:YES];
b19871dd 2260
b4d89997 2261 [pool release];
b19871dd
JF
2262}
2263
36bb2ca2
JF
2264- (void) detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)object title:(NSString *)title {
2265 [navbar_ popNavigationItem];
2266 UINavigationItem *navitem = [[[UINavigationItem alloc] initWithTitle:title] autorelease];
2267 [navbar_ pushNavigationItem:navitem];
b4d89997 2268
36bb2ca2
JF
2269 [status_ setText:nil];
2270 [output_ setText:@""];
2271 [progress_ setProgress:0];
b19871dd 2272
36bb2ca2 2273 [transition_ transition:6 toView:overlay_];
b19871dd 2274
36bb2ca2
JF
2275 [NSThread
2276 detachNewThreadSelector:@selector(_detachNewThreadData:)
2277 toTarget:self
2278 withObject:[[ProgressData alloc]
2279 initWithSelector:selector
2280 target:target
2281 object:object
2282 ]
2283 ];
b19871dd
JF
2284}
2285
36bb2ca2
JF
2286- (void) setProgressError:(NSString *)error {
2287 [self
2288 performSelectorOnMainThread:@selector(_setProgressError:)
2289 withObject:error
2290 waitUntilDone:YES
2291 ];
b19871dd
JF
2292}
2293
36bb2ca2
JF
2294- (void) setProgressTitle:(NSString *)title {
2295 [self
2296 performSelectorOnMainThread:@selector(_setProgressTitle:)
2297 withObject:title
2298 waitUntilDone:YES
2299 ];
b19871dd
JF
2300}
2301
36bb2ca2
JF
2302- (void) setProgressPercent:(float)percent {
2303 [self
2304 performSelectorOnMainThread:@selector(_setProgressPercent:)
2305 withObject:[NSNumber numberWithFloat:percent]
2306 waitUntilDone:YES
2307 ];
b19871dd
JF
2308}
2309
36bb2ca2
JF
2310- (void) addProgressOutput:(NSString *)output {
2311 [self
2312 performSelectorOnMainThread:@selector(_addProgressOutput:)
2313 withObject:output
2314 waitUntilDone:YES
2315 ];
b19871dd
JF
2316}
2317
36bb2ca2
JF
2318- (void) _setProgressError:(NSString *)error {
2319 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
2320 initWithTitle:@"Package Error"
2321 buttons:[NSArray arrayWithObjects:@"Okay", nil]
2322 defaultButtonIndex:0
2323 delegate:self
2324 context:self
2325 ] autorelease];
2326
2327 [sheet setBodyText:error];
2328 [sheet popupAlertAnimated:YES];
b19871dd
JF
2329}
2330
36bb2ca2
JF
2331- (void) _setProgressTitle:(NSString *)title {
2332 [status_ setText:[title stringByAppendingString:@"..."]];
b4d89997 2333}
b19871dd 2334
36bb2ca2
JF
2335- (void) _setProgressPercent:(NSNumber *)percent {
2336 [progress_ setProgress:[percent floatValue]];
49048579
JF
2337}
2338
36bb2ca2
JF
2339- (void) _addProgressOutput:(NSString *)output {
2340 [output_ setText:[NSString stringWithFormat:@"%@\n%@", [output_ text], output]];
2341 CGSize size = [output_ contentSize];
2342 CGRect rect = {{0, size.height}, {size.width, 0}};
2343 [output_ scrollRectToVisible:rect animated:YES];
2344}
dc088e63 2345
36bb2ca2
JF
2346@end
2347/* }}} */
dc088e63 2348
36bb2ca2
JF
2349/* Package Cell {{{ */
2350@interface PackageCell : UITableCell {
2351 UIImageView *icon_;
2352 UITextLabel *name_;
2353 UITextLabel *description_;
2354 UITextLabel *source_;
2355 UIImageView *trusted_;
2356}
723a0072 2357
36bb2ca2
JF
2358- (PackageCell *) init;
2359- (void) setPackage:(Package *)package;
ec97ef06 2360
36bb2ca2
JF
2361- (void) _setSelected:(float)fraction;
2362- (void) setSelected:(BOOL)selected;
2363- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2364- (void) _setSelectionFadeFraction:(float)fraction;
ec97ef06 2365
36bb2ca2 2366@end
49048579 2367
36bb2ca2
JF
2368@implementation PackageCell
2369
2370- (void) dealloc {
2371 [icon_ release];
2372 [name_ release];
2373 [description_ release];
2374 [source_ release];
2375 [trusted_ release];
2376 [super dealloc];
b4d89997 2377}
b19871dd 2378
36bb2ca2
JF
2379- (PackageCell *) init {
2380 if ((self = [super init]) != nil) {
2381 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 20);
2382 GSFontRef large = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
2383 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 14);
b19871dd 2384
36bb2ca2 2385 icon_ = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 30, 30)];
b19871dd 2386
36bb2ca2
JF
2387 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 8, 240, 25)];
2388 [name_ setBackgroundColor:Clear_];
2389 [name_ setFont:bold];
b19871dd 2390
36bb2ca2
JF
2391 source_ = [[UITextLabel alloc] initWithFrame:CGRectMake(58, 28, 225, 20)];
2392 [source_ setBackgroundColor:Clear_];
2393 [source_ setFont:large];
b19871dd 2394
36bb2ca2
JF
2395 description_ = [[UITextLabel alloc] initWithFrame:CGRectMake(12, 46, 280, 20)];
2396 [description_ setBackgroundColor:Clear_];
2397 [description_ setFont:small];
b19871dd 2398
36bb2ca2
JF
2399 trusted_ = [[UIImageView alloc] initWithFrame:CGRectMake(30, 30, 16, 16)];
2400 [trusted_ setImage:[UIImage applicationImageNamed:@"trusted.png"]];
b19871dd 2401
36bb2ca2
JF
2402 [self addSubview:icon_];
2403 [self addSubview:name_];
2404 [self addSubview:description_];
2405 [self addSubview:source_];
2406
2407 CFRelease(small);
2408 CFRelease(large);
2409 CFRelease(bold);
2410 } return self;
b4d89997 2411}
b19871dd 2412
36bb2ca2
JF
2413- (void) setPackage:(Package *)package {
2414 Source *source = [package source];
b19871dd 2415
36bb2ca2
JF
2416 UIImage *image = nil;
2417 if (NSString *icon = [package icon])
2418 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2419 if (image == nil) if (NSString *icon = [source defaultIcon])
2420 image = [UIImage imageAtPath:[icon substringFromIndex:6]];
2421 if (image == nil)
2422 image = [UIImage applicationImageNamed:@"unknown.png"];
36bb2ca2 2423 [icon_ setImage:image];
a0be02eb
JF
2424
2425 if (image != nil) {
2426 CGSize size = [image size];
2427 float scale = 30 / std::max(size.width, size.height);
2428 [icon_ zoomToScale:scale];
2429 }
2430
36bb2ca2 2431 [icon_ setFrame:CGRectMake(10, 10, 30, 30)];
b19871dd 2432
36bb2ca2
JF
2433 [name_ setText:[package name]];
2434 [description_ setText:[package tagline]];
2435
2436 NSString *label;
2437 bool trusted;
b19871dd 2438
36bb2ca2
JF
2439 if (source != nil) {
2440 label = [source label];
2441 trusted = [source trusted];
2442 } else if ([[package id] isEqualToString:@"firmware"]) {
2443 label = @"Apple";
2444 trusted = false;
2445 } else {
2446 label = @"Unknown/Local";
2447 trusted = false;
b4d89997 2448 }
b19871dd 2449
4cf4165e 2450 [source_ setText:[NSString stringWithFormat:@"from %@ (%@)", label, Simplify([package section])]];
36bb2ca2
JF
2451
2452 if (trusted)
2453 [self addSubview:trusted_];
2454 else
2455 [trusted_ removeFromSuperview];
b19871dd
JF
2456}
2457
36bb2ca2
JF
2458- (void) _setSelected:(float)fraction {
2459 CGColor black(space_,
2460 Interpolate(0.0, 1.0, fraction),
2461 Interpolate(0.0, 1.0, fraction),
2462 Interpolate(0.0, 1.0, fraction),
2463 1.0);
b19871dd 2464
36bb2ca2
JF
2465 CGColor gray(space_,
2466 Interpolate(0.4, 1.0, fraction),
2467 Interpolate(0.4, 1.0, fraction),
2468 Interpolate(0.4, 1.0, fraction),
2469 1.0);
b19871dd 2470
36bb2ca2
JF
2471 [name_ setColor:black];
2472 [description_ setColor:gray];
2473 [source_ setColor:black];
b19871dd
JF
2474}
2475
36bb2ca2
JF
2476- (void) setSelected:(BOOL)selected {
2477 [self _setSelected:(selected ? 1.0 : 0.0)];
2478 [super setSelected:selected];
b19871dd
JF
2479}
2480
36bb2ca2
JF
2481- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2482 if (!fade)
2483 [self _setSelected:(selected ? 1.0 : 0.0)];
2484 [super setSelected:selected withFade:fade];
2485}
2486
2487- (void) _setSelectionFadeFraction:(float)fraction {
2488 [self _setSelected:fraction];
2489 [super _setSelectionFadeFraction:fraction];
ec97ef06
JF
2490}
2491
8da60fb7
JF
2492@end
2493/* }}} */
36bb2ca2
JF
2494/* Section Cell {{{ */
2495@interface SectionCell : UITableCell {
2496 UITextLabel *name_;
2497 UITextLabel *count_;
b4d89997 2498}
b19871dd 2499
36bb2ca2
JF
2500- (id) init;
2501- (void) setSection:(Section *)section;
2502
2503- (void) _setSelected:(float)fraction;
2504- (void) setSelected:(BOOL)selected;
2505- (void) setSelected:(BOOL)selected withFade:(BOOL)fade;
2506- (void) _setSelectionFadeFraction:(float)fraction;
b19871dd 2507
8da60fb7
JF
2508@end
2509
36bb2ca2 2510@implementation SectionCell
8da60fb7 2511
36bb2ca2
JF
2512- (void) dealloc {
2513 [name_ release];
2514 [count_ release];
2515 [super dealloc];
2516}
2517
2518- (id) init {
b4d89997 2519 if ((self = [super init]) != nil) {
36bb2ca2
JF
2520 GSFontRef bold = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 22);
2521 GSFontRef small = GSFontCreateWithName("Helvetica", kGSFontTraitBold, 12);
2522
2523 name_ = [[UITextLabel alloc] initWithFrame:CGRectMake(48, 9, 250, 25)];
2524 [name_ setBackgroundColor:Clear_];
2525 [name_ setFont:bold];
2526
2527 count_ = [[UITextLabel alloc] initWithFrame:CGRectMake(11, 7, 29, 32)];
2528 [count_ setCentersHorizontally:YES];
2529 [count_ setBackgroundColor:Clear_];
2530 [count_ setFont:small];
2531 [count_ setColor:White_];
2532
2533 UIImageView *folder = [[[UIImageView alloc] initWithFrame:CGRectMake(8, 7, 32, 32)] autorelease];
2534 [folder setImage:[UIImage applicationImageNamed:@"folder.png"]];
2535
2536 [self addSubview:folder];
2537 [self addSubview:name_];
2538 [self addSubview:count_];
2539
2540 [self _setSelected:0];
2541
2542 CFRelease(small);
2543 CFRelease(bold);
b4d89997 2544 } return self;
b19871dd
JF
2545}
2546
36bb2ca2
JF
2547- (void) setSection:(Section *)section {
2548 if (section == nil) {
2549 [name_ setText:@"All Packages"];
2550 [count_ setText:nil];
2551 } else {
2552 NSString *name = [section name];
2553 [name_ setText:(name == nil ? @"(No Section)" : name)];
2554 [count_ setText:[NSString stringWithFormat:@"%d", [section count]]];
2555 }
b19871dd
JF
2556}
2557
36bb2ca2
JF
2558- (void) _setSelected:(float)fraction {
2559 CGColor black(space_,
2560 Interpolate(0.0, 1.0, fraction),
2561 Interpolate(0.0, 1.0, fraction),
2562 Interpolate(0.0, 1.0, fraction),
2563 1.0);
b19871dd 2564
36bb2ca2 2565 [name_ setColor:black];
b19871dd
JF
2566}
2567
36bb2ca2
JF
2568- (void) setSelected:(BOOL)selected {
2569 [self _setSelected:(selected ? 1.0 : 0.0)];
2570 [super setSelected:selected];
b19871dd
JF
2571}
2572
36bb2ca2
JF
2573- (void) setSelected:(BOOL)selected withFade:(BOOL)fade {
2574 if (!fade)
2575 [self _setSelected:(selected ? 1.0 : 0.0)];
2576 [super setSelected:selected withFade:fade];
2577}
b19871dd 2578
36bb2ca2
JF
2579- (void) _setSelectionFadeFraction:(float)fraction {
2580 [self _setSelected:fraction];
2581 [super _setSelectionFadeFraction:fraction];
2582}
b31b87cc 2583
36bb2ca2
JF
2584@end
2585/* }}} */
b19871dd 2586
36bb2ca2
JF
2587/* Browser Interface {{{ */
2588@interface BrowserView : RVPage {
2589 _transient Database *database_;
2590 UIScroller *scroller_;
2591 UIWebView *webview_;
2592 NSMutableArray *urls_;
2593 UIProgressIndicator *indicator_;
b19871dd 2594
36bb2ca2
JF
2595 NSString *title_;
2596 bool loading_;
2597}
b19871dd 2598
36bb2ca2
JF
2599- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy;
2600- (void) loadURL:(NSURL *)url;
b19871dd 2601
36bb2ca2
JF
2602- (void) loadRequest:(NSURLRequest *)request;
2603- (void) reloadURL;
b19871dd 2604
36bb2ca2 2605- (WebView *) webView;
8da60fb7 2606
36bb2ca2 2607- (id) initWithBook:(RVBook *)book database:(Database *)database;
b4d89997 2608
b4d89997 2609@end
36bb2ca2 2610/* }}} */
b4d89997 2611
36bb2ca2
JF
2612/* Package View {{{ */
2613@protocol PackageViewDelegate
2614- (void) performPackage:(Package *)package;
2615@end
8da60fb7 2616
36bb2ca2
JF
2617@interface PackageView : RVPage {
2618 _transient Database *database_;
2619 UIPreferencesTable *table_;
2620 Package *package_;
2621 NSString *name_;
2622 UITextView *description_;
b31b87cc
JF
2623}
2624
36bb2ca2
JF
2625- (id) initWithBook:(RVBook *)book database:(Database *)database;
2626- (void) setPackage:(Package *)package;
b4d89997 2627
36bb2ca2 2628@end
b4d89997 2629
36bb2ca2 2630@implementation PackageView
b4d89997 2631
36bb2ca2
JF
2632- (void) dealloc {
2633 [table_ setDataSource:nil];
2634 [table_ setDelegate:nil];
b4d89997 2635
36bb2ca2
JF
2636 if (package_ != nil)
2637 [package_ release];
2638 if (name_ != nil)
2639 [name_ release];
2640 if (description_ != nil)
2641 [description_ release];
2642 [table_ release];
2643 [super dealloc];
2644}
8da60fb7 2645
36bb2ca2
JF
2646- (int) numberOfGroupsInPreferencesTable:(UIPreferencesTable *)table {
2647 return [package_ source] == nil ? 2 : 3;
2648}
b31b87cc 2649
36bb2ca2
JF
2650- (NSString *) preferencesTable:(UIPreferencesTable *)table titleForGroup:(int)group {
2651 switch (group) {
2652 case 0: return nil;
2653 case 1: return @"Package Details";
2654 case 2: return @"Source Information";
b31b87cc 2655
36bb2ca2
JF
2656 default: _assert(false);
2657 }
2658}
8da60fb7 2659
36bb2ca2
JF
2660- (float) preferencesTable:(UIPreferencesTable *)table heightForRow:(int)row inGroup:(int)group withProposedHeight:(float)proposed {
2661 if (group != 0 || row != 1)
2662 return proposed;
2663 else
2664 return [description_ visibleTextRect].size.height + TextViewOffset_;
2665}
8da60fb7 2666
36bb2ca2
JF
2667- (int) preferencesTable:(UIPreferencesTable *)table numberOfRowsInGroup:(int)group {
2668 switch (group) {
2669 case 0: return [package_ website] == nil ? 2 : 3;
2670 case 1: return 5;
2671 case 2: return 3;
8da60fb7 2672
36bb2ca2
JF
2673 default: _assert(false);
2674 }
2675}
8da60fb7 2676
36bb2ca2
JF
2677- (UIPreferencesTableCell *) preferencesTable:(UIPreferencesTable *)table cellForRow:(int)row inGroup:(int)group {
2678 UIPreferencesTableCell *cell = [[[UIPreferencesTableCell alloc] init] autorelease];
2679 [cell setShowSelection:NO];
b4d89997 2680
36bb2ca2
JF
2681 switch (group) {
2682 case 0: switch (row) {
2683 case 0:
2684 [cell setTitle:[package_ name]];
2685 [cell setValue:[package_ latest]];
2686 break;
b4d89997 2687
36bb2ca2
JF
2688 case 1:
2689 [cell addSubview:description_];
2690 break;
b4d89997 2691
36bb2ca2
JF
2692 case 2:
2693 [cell setTitle:@"More Information"];
2694 [cell setShowDisclosure:YES];
2695 [cell setShowSelection:YES];
2696 break;
8da60fb7 2697
36bb2ca2
JF
2698 default: _assert(false);
2699 } break;
b19871dd 2700
36bb2ca2
JF
2701 case 1: switch (row) {
2702 case 0:
2703 [cell setTitle:@"Identifier"];
2704 [cell setValue:[package_ id]];
2705 break;
b19871dd 2706
36bb2ca2
JF
2707 case 1: {
2708 [cell setTitle:@"Installed Version"];
2709 NSString *installed([package_ installed]);
2710 [cell setValue:(installed == nil ? @"n/a" : installed)];
2711 } break;
b19871dd 2712
36bb2ca2
JF
2713 case 2: {
2714 [cell setTitle:@"Section"];
2715 NSString *section([package_ section]);
2716 [cell setValue:(section == nil ? @"n/a" : section)];
2717 } break;
8da60fb7 2718
36bb2ca2
JF
2719 case 3:
2720 [cell setTitle:@"Expanded Size"];
2721 [cell setValue:SizeString([package_ size])];
2722 break;
8da60fb7 2723
36bb2ca2
JF
2724 case 4:
2725 [cell setTitle:@"Maintainer"];
2726 [cell setValue:[[package_ maintainer] name]];
2727 [cell setShowDisclosure:YES];
2728 [cell setShowSelection:YES];
2729 break;
8da60fb7 2730
36bb2ca2
JF
2731 default: _assert(false);
2732 } break;
3178d79b 2733
36bb2ca2
JF
2734 case 2: switch (row) {
2735 case 0:
2736 [cell setTitle:[[package_ source] label]];
2737 [cell setValue:[[package_ source] version]];
2738 break;
3178d79b 2739
36bb2ca2
JF
2740 case 1:
2741 [cell setValue:[[package_ source] description]];
2742 break;
3178d79b 2743
36bb2ca2
JF
2744 case 2:
2745 [cell setTitle:@"Origin"];
2746 [cell setValue:[[package_ source] origin]];
2747 break;
3178d79b 2748
36bb2ca2
JF
2749 default: _assert(false);
2750 } break;
8da60fb7 2751
36bb2ca2
JF
2752 default: _assert(false);
2753 }
dc5812ec 2754
36bb2ca2 2755 return cell;
b4d89997 2756}
dc5812ec 2757
36bb2ca2
JF
2758- (BOOL) canSelectRow:(int)row {
2759 return YES;
2367a917
JF
2760}
2761
36bb2ca2
JF
2762- (void) tableRowSelected:(NSNotification *)notification {
2763 int row = [table_ selectedRow];
2764 NSString *website = [package_ website];
2367a917 2765
36bb2ca2
JF
2766 if (row == (website == nil ? 8 : 9))
2767 [delegate_ openURL:[NSURL URLWithString:[NSString stringWithFormat:@"mailto:%@?subject=%@",
2768 [[package_ maintainer] email],
2769 [[NSString stringWithFormat:@"regarding apt package \"%@\"", [package_ name]] stringByAddingPercentEscapes]
2770 ]]];
2771 else if (website != nil && row == 3) {
2772 NSURL *url = [NSURL URLWithString:website];
2773 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
2774 [browser setDelegate:delegate_];
2775 [book_ pushPage:browser];
2776 [browser loadURL:url];
2777 }
3178d79b
JF
2778}
2779
36bb2ca2
JF
2780- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
2781 switch (button) {
2782 case 1: [delegate_ installPackage:package_]; break;
2783 case 2: [delegate_ removePackage:package_]; break;
2784 }
2785
2786 [sheet dismiss];
b4d89997 2787}
2367a917 2788
36bb2ca2
JF
2789- (void) _rightButtonClicked {
2790 if ([package_ installed] == nil)
2791 [delegate_ installPackage:package_];
2792 else {
2793 NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:6];
2367a917 2794
36bb2ca2
JF
2795 if ([package_ upgradable])
2796 [buttons addObject:@"Upgrade"];
2797 else
2798 [buttons addObject:@"Reinstall"];
2799
2800 [buttons addObject:@"Remove"];
2801 [buttons addObject:@"Cancel"];
2802
2803 [delegate_ slideUp:[[[UIAlertSheet alloc]
2804 initWithTitle:@"Manage Package"
2805 buttons:buttons
2806 defaultButtonIndex:2
2807 delegate:self
2808 context:self
2809 ] autorelease]];
2810 }
b4d89997 2811}
2367a917 2812
36bb2ca2
JF
2813- (NSString *) rightButtonTitle {
2814 _assert(package_ != nil);
559dec34 2815 return [package_ installed] == nil ? @"Install" : @"Modify";
b4d89997 2816}
2367a917 2817
36bb2ca2
JF
2818- (NSString *) title {
2819 return @"Details";
dc5812ec
JF
2820}
2821
36bb2ca2
JF
2822- (id) initWithBook:(RVBook *)book database:(Database *)database {
2823 if ((self = [super initWithBook:book]) != nil) {
2824 database_ = database;
2825
2826 table_ = [[UIPreferencesTable alloc] initWithFrame:[self bounds]];
2827 [self addSubview:table_];
2828
2829 [table_ setDataSource:self];
2830 [table_ setDelegate:self];
2831 } return self;
dc5812ec
JF
2832}
2833
36bb2ca2
JF
2834- (void) setPackage:(Package *)package {
2835 if (package_ != nil) {
2836 [package_ autorelease];
2837 package_ = nil;
2838 }
2839
2840 if (name_ != nil) {
2841 [name_ release];
2842 name_ = nil;
2843 }
2844
2845 if (description_ != nil) {
2846 [description_ release];
2847 description_ = nil;
2848 }
2849
2850 if (package != nil) {
2851 package_ = [package retain];
2852 name_ = [[package id] retain];
2853
2854 NSString *description([package description]);
2855 if (description == nil)
2856 description = [package tagline];
2857 description_ = [GetTextView(description, 12, true) retain];
2858
2859 [description_ setTextColor:Black_];
2860
2861 [table_ reloadData];
2862 }
dc5812ec
JF
2863}
2864
36bb2ca2
JF
2865- (void) resetViewAnimated:(BOOL)animated {
2866 [table_ resetViewAnimated:animated];
dc5812ec
JF
2867}
2868
36bb2ca2
JF
2869- (void) reloadData {
2870 [self setPackage:[database_ packageWithName:name_]];
2871 [self reloadButtons];
8fe19fc1
JF
2872}
2873
b4d89997
JF
2874@end
2875/* }}} */
b4d89997 2876/* Package Table {{{ */
36bb2ca2
JF
2877@interface PackageTable : RVPage {
2878 _transient Database *database_;
2879 NSString *title_;
2880 SEL filter_;
2881 id object_;
2882 NSMutableArray *packages_;
b4d89997 2883 NSMutableArray *sections_;
36bb2ca2 2884 UISectionList *list_;
dc5812ec
JF
2885}
2886
36bb2ca2 2887- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object;
b4d89997
JF
2888
2889- (void) setDelegate:(id)delegate;
a0be02eb
JF
2890- (void) setObject:(id)object;
2891
36bb2ca2 2892- (void) reloadData;
a0be02eb 2893- (void) resetCursor;
b4d89997 2894
b4d89997
JF
2895@end
2896
2897@implementation PackageTable
2898
2899- (void) dealloc {
36bb2ca2
JF
2900 [list_ setDataSource:nil];
2901
2902 [title_ release];
2903 if (object_ != nil)
2904 [object_ release];
2905 [packages_ release];
b4d89997 2906 [sections_ release];
36bb2ca2 2907 [list_ release];
b4d89997 2908 [super dealloc];
8fe19fc1
JF
2909}
2910
b4d89997
JF
2911- (int) numberOfSectionsInSectionList:(UISectionList *)list {
2912 return [sections_ count];
2913}
2367a917 2914
b4d89997
JF
2915- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
2916 return [[sections_ objectAtIndex:section] name];
2917}
dc5812ec 2918
b4d89997
JF
2919- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
2920 return [[sections_ objectAtIndex:section] row];
2367a917
JF
2921}
2922
b4d89997
JF
2923- (int) numberOfRowsInTable:(UITable *)table {
2924 return [packages_ count];
2925}
dc5812ec 2926
b4d89997 2927- (float) table:(UITable *)table heightForRow:(int)row {
36bb2ca2 2928 return 73;
b4d89997 2929}
dc5812ec 2930
b4d89997
JF
2931- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
2932 if (reusing == nil)
36bb2ca2 2933 reusing = [[[PackageCell alloc] init] autorelease];
b4d89997
JF
2934 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
2935 return reusing;
2936}
dc5812ec 2937
b4d89997
JF
2938- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
2939 return NO;
2940}
dc5812ec 2941
b4d89997
JF
2942- (void) tableRowSelected:(NSNotification *)notification {
2943 int row = [[notification object] selectedRow];
36bb2ca2
JF
2944 if (row == INT_MAX)
2945 return;
2946
2947 Package *package = [packages_ objectAtIndex:row];
2948 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
2949 [view setDelegate:delegate_];
2950 [view setPackage:package];
2951 [book_ pushPage:view];
dc5812ec
JF
2952}
2953
36bb2ca2
JF
2954- (id) initWithBook:(RVBook *)book database:(Database *)database title:(NSString *)title filter:(SEL)filter with:(id)object {
2955 if ((self = [super initWithBook:book]) != nil) {
2956 database_ = database;
2957 title_ = [title retain];
2958 filter_ = filter;
2959 object_ = object == nil ? nil : [object retain];
2960
2961 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
b4d89997 2962 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 2963
b4d89997
JF
2964 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:YES];
2965 [list_ setDataSource:self];
dc5812ec 2966
b4d89997
JF
2967 UITableColumn *column = [[[UITableColumn alloc]
2968 initWithTitle:@"Name"
2969 identifier:@"name"
36bb2ca2 2970 width:[self frame].size.width
b4d89997
JF
2971 ] autorelease];
2972
2973 UITable *table = [list_ table];
2974 [table setSeparatorStyle:1];
2975 [table addTableColumn:column];
2976 [table setDelegate:self];
2977 [table setReusesTableCells:YES];
2978
2979 [self addSubview:list_];
36bb2ca2 2980 [self reloadData];
b4d89997 2981 } return self;
dc5812ec
JF
2982}
2983
2984- (void) setDelegate:(id)delegate {
2367a917 2985 delegate_ = delegate;
b4d89997
JF
2986}
2987
a0be02eb
JF
2988- (void) setObject:(id)object {
2989 if (object_ != nil)
2990 [object_ release];
2991 if (object == nil)
2992 object_ = nil;
2993 else
2994 object_ = [object retain];
2995}
2996
36bb2ca2
JF
2997- (void) reloadData {
2998 NSArray *packages = [database_ packages];
b4d89997 2999
36bb2ca2 3000 [packages_ removeAllObjects];
b4d89997
JF
3001 [sections_ removeAllObjects];
3002
36bb2ca2
JF
3003 for (size_t i(0); i != [packages count]; ++i) {
3004 Package *package([packages objectAtIndex:i]);
3005 if ([[package performSelector:filter_ withObject:object_] boolValue])
3006 [packages_ addObject:package];
3007 }
3008
3009 [packages_ sortUsingSelector:@selector(compareByName:)];
3010
b4d89997
JF
3011 Section *section = nil;
3012
3013 for (size_t offset(0); offset != [packages_ count]; ++offset) {
3014 Package *package = [packages_ objectAtIndex:offset];
3015 NSString *name = [package index];
3016
36bb2ca2 3017 if (section == nil || ![[section name] isEqualToString:name]) {
b4d89997
JF
3018 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3019 [sections_ addObject:section];
3020 }
3021
36bb2ca2 3022 [section addToCount];
b4d89997
JF
3023 }
3024
3025 [list_ reloadData];
3026}
3027
36bb2ca2
JF
3028- (NSString *) title {
3029 return title_;
b4d89997
JF
3030}
3031
36bb2ca2
JF
3032- (void) resetViewAnimated:(BOOL)animated {
3033 [list_ resetViewAnimated:animated];
dc5812ec
JF
3034}
3035
a0be02eb
JF
3036- (void) resetCursor {
3037 [[list_ table] scrollPointVisibleAtTopLeft:CGPointMake(0, 0) animated:NO];
3038}
3039
dc5812ec 3040@end
b4d89997 3041/* }}} */
dc5812ec 3042
36bb2ca2
JF
3043/* Browser Implementation {{{ */
3044@implementation BrowserView
3045
3046- (void) dealloc {
3047 WebView *webview = [webview_ webView];
3048 [webview setFrameLoadDelegate:nil];
3049 [webview setResourceLoadDelegate:nil];
3050 [webview setUIDelegate:nil];
3051
3052 [scroller_ setDelegate:nil];
3053 [webview_ setDelegate:nil];
3054
3055 [scroller_ release];
3056 [webview_ release];
3057 [urls_ release];
3058 [indicator_ release];
3059 if (title_ != nil)
3060 [title_ release];
3061 [super dealloc];
dc5812ec
JF
3062}
3063
36bb2ca2
JF
3064- (void) loadURL:(NSURL *)url cachePolicy:(NSURLRequestCachePolicy)policy {
3065 NSMutableURLRequest *request = [NSMutableURLRequest
3066 requestWithURL:url
3067 cachePolicy:policy
3068 timeoutInterval:30.0
3069 ];
b4d89997 3070
36bb2ca2
JF
3071 [request addValue:[NSString stringWithCString:Firmware_] forHTTPHeaderField:@"X-Firmware"];
3072 [request addValue:[NSString stringWithCString:Machine_] forHTTPHeaderField:@"X-Machine"];
3073 [request addValue:[NSString stringWithCString:SerialNumber_] forHTTPHeaderField:@"X-Serial-Number"];
b4d89997 3074
36bb2ca2
JF
3075 [self loadRequest:request];
3076}
dc5812ec 3077
dc5812ec 3078
36bb2ca2
JF
3079- (void) loadURL:(NSURL *)url {
3080 [self loadURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy];
3081}
dc5812ec 3082
36bb2ca2
JF
3083// XXX: this needs to add the headers
3084- (NSURLRequest *) _addHeadersToRequest:(NSURLRequest *)request {
3085 return request;
b4d89997
JF
3086}
3087
36bb2ca2
JF
3088- (void) loadRequest:(NSURLRequest *)request {
3089 [webview_ loadRequest:request];
3090}
b4d89997 3091
36bb2ca2
JF
3092- (void) reloadURL {
3093 NSURL *url = [[[urls_ lastObject] retain] autorelease];
3094 [urls_ removeLastObject];
3095 [self loadURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData];
3096}
b4d89997 3097
36bb2ca2
JF
3098- (WebView *) webView {
3099 return [webview_ webView];
3100}
b4d89997 3101
36bb2ca2
JF
3102- (void) view:(UIView *)sender didSetFrame:(CGRect)frame {
3103 [scroller_ setContentSize:frame.size];
3104}
b4d89997 3105
36bb2ca2
JF
3106- (void) view:(UIView *)sender didSetFrame:(CGRect)frame oldFrame:(CGRect)old {
3107 [self view:sender didSetFrame:frame];
3108}
b4d89997 3109
36bb2ca2
JF
3110- (NSURLRequest *) webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource {
3111 return [self _addHeadersToRequest:request];
3112}
b4d89997 3113
36bb2ca2
JF
3114- (WebView *) webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request {
3115 [self setBackButtonTitle:title_];
3116 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
3117 [browser setDelegate:delegate_];
3118 [book_ pushPage:browser];
3119 [browser loadRequest:[self _addHeadersToRequest:request]];
3120 return [browser webView];
3121}
3122
3123- (void) webView:(WebView *)sender willClickElement:(id)element {
3124 if (![element respondsToSelector:@selector(href)])
3125 return;
3126 NSString *href = [element href];
3127 if (href == nil)
3128 return;
3129 if ([href hasPrefix:@"apptapp://package/"]) {
3130 NSString *name = [href substringFromIndex:18];
3131 Package *package = [database_ packageWithName:name];
3132 if (package == nil) {
3133 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3134 initWithTitle:@"Cannot Locate Package"
3135 buttons:[NSArray arrayWithObjects:@"Close", nil]
3136 defaultButtonIndex:0
3137 delegate:self
3138 context:self
3139 ] autorelease];
b4d89997 3140
36bb2ca2
JF
3141 [sheet setBodyText:[NSString stringWithFormat:
3142 @"The package %@ cannot be found in your current sources. I might recommend intalling more sources."
3143 , name]];
b4d89997 3144
36bb2ca2
JF
3145 [sheet popupAlertAnimated:YES];
3146 } else {
3147 [self setBackButtonTitle:title_];
3148 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3149 [view setDelegate:delegate_];
3150 [view setPackage:package];
3151 [book_ pushPage:view];
3152 }
3153 }
dc5812ec
JF
3154}
3155
36bb2ca2
JF
3156- (void) webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame {
3157 title_ = [title retain];
3158 [self setTitle:title];
3159}
3160
3161- (void) webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame {
3162 if ([frame parentFrame] != nil)
3163 return;
3164
3165 loading_ = true;
3166 [indicator_ startAnimation];
3167 [self reloadButtons];
3168
3169 if (title_ != nil) {
3170 [title_ release];
3171 title_ = nil;
b4d89997 3172 }
36bb2ca2
JF
3173
3174 [self setTitle:@"Loading..."];
3175
3176 WebView *webview = [webview_ webView];
3177 NSString *href = [webview mainFrameURL];
3178 [urls_ addObject:[NSURL URLWithString:href]];
3179
3180 CGRect webrect = [scroller_ frame];
3181 webrect.size.height = 0;
3182 [webview_ setFrame:webrect];
dc5812ec
JF
3183}
3184
36bb2ca2
JF
3185- (void) _finishLoading {
3186 loading_ = false;
3187 [indicator_ stopAnimation];
3188 [self reloadButtons];
3189}
b4d89997 3190
36bb2ca2
JF
3191- (void) webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
3192 if ([frame parentFrame] != nil)
3193 return;
3194 [self _finishLoading];
3195}
b4d89997 3196
36bb2ca2
JF
3197- (void) webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame {
3198 if ([frame parentFrame] != nil)
3199 return;
3200 [self setTitle:[error localizedDescription]];
3201 [self _finishLoading];
3202}
3203
3204- (id) initWithBook:(RVBook *)book database:(Database *)database {
3205 if ((self = [super initWithBook:book]) != nil) {
3206 database_ = database;
3207 loading_ = false;
3208
3209 struct CGRect bounds = [self bounds];
3210
3211 UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:bounds] autorelease];
3212 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
3213 [self addSubview:pinstripe];
3214
3215 scroller_ = [[UIScroller alloc] initWithFrame:bounds];
3216 [self addSubview:scroller_];
3217
3218 [scroller_ setScrollingEnabled:YES];
3219 [scroller_ setAdjustForContentSizeChange:YES];
3220 [scroller_ setClipsSubviews:YES];
3221 [scroller_ setAllowsRubberBanding:YES];
3222 [scroller_ setScrollDecelerationFactor:0.99];
3223 [scroller_ setDelegate:self];
3224
3225 CGRect webrect = [scroller_ bounds];
3226 webrect.size.height = 0;
3227
3228 webview_ = [[UIWebView alloc] initWithFrame:webrect];
3229 [scroller_ addSubview:webview_];
3230
3231 [webview_ setTilingEnabled:YES];
3232 [webview_ setTileSize:CGSizeMake(webrect.size.width, 500)];
3233 [webview_ setAutoresizes:YES];
3234 [webview_ setDelegate:self];
3235 //[webview_ setEnabledGestures:2];
b4d89997 3236
36bb2ca2
JF
3237 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:0];
3238 indicator_ = [[UIProgressIndicator alloc] initWithFrame:CGRectMake(281, 43, indsize.width, indsize.height)];
3239 [indicator_ setStyle:0];
3240
3241 Package *package([database_ packageWithName:@"cydia"]);
3242 NSString *application = package == nil ? @"Cydia" : [NSString
3243 stringWithFormat:@"Cydia/%@",
3244 [package installed]
3245 ];
3246
3247 WebView *webview = [webview_ webView];
3248 [webview setApplicationNameForUserAgent:application];
3249 [webview setFrameLoadDelegate:self];
3250 [webview setResourceLoadDelegate:self];
3251 [webview setUIDelegate:self];
3252
3253 urls_ = [[NSMutableArray alloc] initWithCapacity:16];
3254 } return self;
dc5812ec
JF
3255}
3256
36bb2ca2
JF
3257- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
3258 [sheet dismiss];
3259}
3260
3261- (void) _leftButtonClicked {
3262 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
3263 initWithTitle:@"About Cydia Packager"
3264 buttons:[NSArray arrayWithObjects:@"Close", nil]
3265 defaultButtonIndex:0
3266 delegate:self
3267 context:self
3268 ] autorelease];
3269
3270 [sheet setBodyText:
3271 @"Copyright (C) 2008\n"
3272 "Jay Freeman (saurik)\n"
3273 "saurik@saurik.com\n"
3274 "http://www.saurik.com/\n"
3275 "\n"
3276 "The Okori Group\n"
3277 "http://www.theokorigroup.com/\n"
3278 "\n"
3279 "College of Creative Studies,\n"
3280 "University of California,\n"
3281 "Santa Barbara\n"
3282 "http://www.ccs.ucsb.edu/"
3283 ];
3284
3285 [sheet popupAlertAnimated:YES];
3286}
3287
3288- (void) _rightButtonClicked {
3289 [self reloadURL];
3290}
3291
3292- (NSString *) leftButtonTitle {
3293 return @"About";
3294}
3295
3296- (NSString *) rightButtonTitle {
3297 return loading_ ? @"" : @"Reload";
3298}
3299
3300- (NSString *) title {
3301 return nil;
3302}
3303
3304- (NSString *) backButtonTitle {
3305 return @"Browser";
dc5812ec
JF
3306}
3307
36bb2ca2
JF
3308- (void) setPageActive:(BOOL)active {
3309 if (active)
3310 [book_ addSubview:indicator_];
3311 else
3312 [indicator_ removeFromSuperview];
dc5812ec
JF
3313}
3314
36bb2ca2 3315- (void) resetViewAnimated:(BOOL)animated {
b4d89997 3316}
2367a917 3317
b4d89997
JF
3318@end
3319/* }}} */
36bb2ca2 3320
9a7b04c5
JF
3321@interface CYBook : RVBook <
3322 ProgressDelegate
3323> {
3324 _transient Database *database_;
3325 UIView *overlay_;
3326 UIProgressIndicator *indicator_;
3327 UITextLabel *prompt_;
3328 UIProgressBar *progress_;
3329 bool updating_;
3330}
3331
3332- (id) initWithFrame:(CGRect)frame database:(Database *)database;
3333- (void) update;
3334- (BOOL) updating;
3335
3336@end
3337
b4d89997 3338/* Install View {{{ */
36bb2ca2
JF
3339@interface InstallView : RVPage {
3340 _transient Database *database_;
b4d89997 3341 NSMutableArray *packages_;
36bb2ca2
JF
3342 NSMutableArray *sections_;
3343 UITable *list_;
b4d89997 3344}
dc5812ec 3345
36bb2ca2
JF
3346- (id) initWithBook:(RVBook *)book database:(Database *)database;
3347- (void) reloadData;
dc5812ec 3348
2367a917
JF
3349@end
3350
b4d89997 3351@implementation InstallView
dc5812ec 3352
2367a917 3353- (void) dealloc {
36bb2ca2
JF
3354 [list_ setDataSource:nil];
3355 [list_ setDelegate:nil];
3356
b4d89997 3357 [packages_ release];
36bb2ca2
JF
3358 [sections_ release];
3359 [list_ release];
2367a917
JF
3360 [super dealloc];
3361}
3362
b4d89997
JF
3363- (int) numberOfRowsInTable:(UITable *)table {
3364 return [sections_ count] + 1;
3365}
8da60fb7 3366
b4d89997
JF
3367- (float) table:(UITable *)table heightForRow:(int)row {
3368 return 45;
3369}
8da60fb7 3370
b4d89997
JF
3371- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3372 if (reusing == nil)
3373 reusing = [[[SectionCell alloc] init] autorelease];
36bb2ca2 3374 [(SectionCell *)reusing setSection:(row == 0 ? nil : [sections_ objectAtIndex:(row - 1)])];
b4d89997
JF
3375 return reusing;
3376}
dc5812ec 3377
b4d89997
JF
3378- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
3379 return YES;
3380}
dc5812ec 3381
b4d89997
JF
3382- (void) tableRowSelected:(NSNotification *)notification {
3383 int row = [[notification object] selectedRow];
36bb2ca2
JF
3384 if (row == INT_MAX)
3385 return;
dc5812ec 3386
36bb2ca2
JF
3387 Section *section;
3388 NSString *title;
dc5812ec 3389
36bb2ca2
JF
3390 if (row == 0) {
3391 section = nil;
3392 title = @"All Packages";
b4d89997 3393 } else {
36bb2ca2
JF
3394 section = [sections_ objectAtIndex:(row - 1)];
3395 title = [section name];
b4d89997 3396 }
2367a917 3397
36bb2ca2
JF
3398 PackageTable *table = [[[PackageTable alloc]
3399 initWithBook:book_
3400 database:database_
3401 title:title
3402 filter:@selector(isUninstalledInSection:)
3403 with:(section == nil ? nil : [section name])
3404 ] autorelease];
2367a917 3405
36bb2ca2 3406 [table setDelegate:delegate_];
2367a917 3407
36bb2ca2 3408 [book_ pushPage:table];
b4d89997 3409}
2367a917 3410
36bb2ca2
JF
3411- (id) initWithBook:(RVBook *)book database:(Database *)database {
3412 if ((self = [super initWithBook:book]) != nil) {
3413 database_ = database;
3414
b4d89997 3415 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
36bb2ca2 3416 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
2367a917 3417
36bb2ca2
JF
3418 list_ = [[UITable alloc] initWithFrame:[self bounds]];
3419 [self addSubview:list_];
dc5812ec 3420
b4d89997
JF
3421 UITableColumn *column = [[[UITableColumn alloc]
3422 initWithTitle:@"Name"
3423 identifier:@"name"
36bb2ca2 3424 width:[self frame].size.width
b4d89997 3425 ] autorelease];
dc5812ec 3426
b4d89997
JF
3427 [list_ setDataSource:self];
3428 [list_ setSeparatorStyle:1];
3429 [list_ addTableColumn:column];
3430 [list_ setDelegate:self];
3431 [list_ setReusesTableCells:YES];
2367a917 3432
36bb2ca2 3433 [self reloadData];
b4d89997 3434 } return self;
2367a917
JF
3435}
3436
36bb2ca2
JF
3437- (void) reloadData {
3438 NSArray *packages = [database_ packages];
3439
b4d89997 3440 [packages_ removeAllObjects];
36bb2ca2 3441 [sections_ removeAllObjects];
dc5812ec 3442
b4d89997
JF
3443 for (size_t i(0); i != [packages count]; ++i) {
3444 Package *package([packages objectAtIndex:i]);
3445 if ([package installed] == nil)
3446 [packages_ addObject:package];
3447 }
dc5812ec 3448
36bb2ca2 3449 [packages_ sortUsingSelector:@selector(compareBySection:)];
dc5812ec 3450
b4d89997
JF
3451 Section *section = nil;
3452 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3453 Package *package = [packages_ objectAtIndex:offset];
3454 NSString *name = [package section];
2367a917 3455
36bb2ca2 3456 if (section == nil || name != nil && ![[section name] isEqualToString:name]) {
b4d89997 3457 section = [[[Section alloc] initWithName:name row:offset] autorelease];
36bb2ca2 3458 [sections_ addObject:section];
b4d89997 3459 }
dc5812ec 3460
36bb2ca2 3461 [section addToCount];
b4d89997 3462 }
2367a917 3463
b4d89997 3464 [list_ reloadData];
36bb2ca2 3465}
2367a917 3466
36bb2ca2
JF
3467- (void) resetViewAnimated:(BOOL)animated {
3468 [list_ resetViewAnimated:animated];
3469}
2367a917 3470
36bb2ca2
JF
3471- (NSString *) title {
3472 return @"Install";
2367a917
JF
3473}
3474
36bb2ca2
JF
3475- (NSString *) backButtonTitle {
3476 return @"Sections";
dc5812ec
JF
3477}
3478
3479@end
2367a917 3480/* }}} */
b4d89997 3481/* Changes View {{{ */
36bb2ca2
JF
3482@interface ChangesView : RVPage {
3483 _transient Database *database_;
dc5812ec
JF
3484 NSMutableArray *packages_;
3485 NSMutableArray *sections_;
36bb2ca2
JF
3486 UISectionList *list_;
3487 unsigned upgrades_;
dc5812ec
JF
3488}
3489
36bb2ca2
JF
3490- (id) initWithBook:(RVBook *)book database:(Database *)database;
3491- (void) reloadData;
dc5812ec 3492
dc5812ec
JF
3493@end
3494
b4d89997
JF
3495@implementation ChangesView
3496
3497- (void) dealloc {
36bb2ca2
JF
3498 [[list_ table] setDelegate:nil];
3499 [list_ setDataSource:nil];
3500
b4d89997
JF
3501 [packages_ release];
3502 [sections_ release];
36bb2ca2 3503 [list_ release];
b4d89997
JF
3504 [super dealloc];
3505}
dc5812ec
JF
3506
3507- (int) numberOfSectionsInSectionList:(UISectionList *)list {
3508 return [sections_ count];
3509}
3510
3511- (NSString *) sectionList:(UISectionList *)list titleForSection:(int)section {
3512 return [[sections_ objectAtIndex:section] name];
3513}
3514
3515- (int) sectionList:(UISectionList *)list rowForSection:(int)section {
3516 return [[sections_ objectAtIndex:section] row];
3517}
3518
3519- (int) numberOfRowsInTable:(UITable *)table {
3520 return [packages_ count];
3521}
3522
3523- (float) table:(UITable *)table heightForRow:(int)row {
36bb2ca2 3524 return 73;
dc5812ec
JF
3525}
3526
1d80f6b9
JF
3527- (UITableCell *) table:(UITable *)table cellForRow:(int)row column:(UITableColumn *)col reusing:(UITableCell *)reusing {
3528 if (reusing == nil)
36bb2ca2 3529 reusing = [[[PackageCell alloc] init] autorelease];
1d80f6b9
JF
3530 [(PackageCell *)reusing setPackage:[packages_ objectAtIndex:row]];
3531 return reusing;
dc5812ec
JF
3532}
3533
3534- (BOOL) table:(UITable *)table showDisclosureForRow:(int)row {
b4d89997 3535 return NO;
dc5812ec
JF
3536}
3537
b4d89997
JF
3538- (void) tableRowSelected:(NSNotification *)notification {
3539 int row = [[notification object] selectedRow];
36bb2ca2
JF
3540 if (row == INT_MAX)
3541 return;
3542 Package *package = [packages_ objectAtIndex:row];
3543 PackageView *view = [[[PackageView alloc] initWithBook:book_ database:database_] autorelease];
3544 [view setDelegate:delegate_];
3545 [view setPackage:package];
3546 [book_ pushPage:view];
dc5812ec
JF
3547}
3548
9a7b04c5
JF
3549- (void) _leftButtonClicked {
3550 [(CYBook *)book_ update];
3551 [self reloadButtons];
3552}
3553
36bb2ca2
JF
3554- (void) _rightButtonClicked {
3555 [delegate_ distUpgrade];
dc5812ec
JF
3556}
3557
36bb2ca2
JF
3558- (id) initWithBook:(RVBook *)book database:(Database *)database {
3559 if ((self = [super initWithBook:book]) != nil) {
3560 database_ = database;
dc5812ec 3561
b4d89997
JF
3562 packages_ = [[NSMutableArray arrayWithCapacity:16] retain];
3563 sections_ = [[NSMutableArray arrayWithCapacity:16] retain];
dc5812ec 3564
36bb2ca2
JF
3565 list_ = [[UISectionList alloc] initWithFrame:[self bounds] showSectionIndex:NO];
3566 [self addSubview:list_];
3567
3178d79b 3568 [list_ setShouldHideHeaderInShortLists:NO];
b4d89997
JF
3569 [list_ setDataSource:self];
3570 //[list_ setSectionListStyle:1];
dc5812ec 3571
b4d89997 3572 UITableColumn *column = [[[UITableColumn alloc]
dc5812ec
JF
3573 initWithTitle:@"Name"
3574 identifier:@"name"
36bb2ca2 3575 width:[self frame].size.width
b4d89997 3576 ] autorelease];
dc5812ec
JF
3577
3578 UITable *table = [list_ table];
3579 [table setSeparatorStyle:1];
3580 [table addTableColumn:column];
3581 [table setDelegate:self];
1d80f6b9 3582 [table setReusesTableCells:YES];
dc5812ec 3583
36bb2ca2 3584 [self reloadData];
dc5812ec
JF
3585 } return self;
3586}
3587
36bb2ca2
JF
3588- (void) reloadData {
3589 NSArray *packages = [database_ packages];
3590
b4d89997 3591 [packages_ removeAllObjects];
36bb2ca2
JF
3592 [sections_ removeAllObjects];
3593
b4d89997
JF
3594 for (size_t i(0); i != [packages count]; ++i) {
3595 Package *package([packages objectAtIndex:i]);
3596 if ([package installed] == nil || [package upgradable])
3597 [packages_ addObject:package];
3598 }
2367a917 3599
b4d89997 3600 [packages_ sortUsingSelector:@selector(compareForChanges:)];
dc5812ec 3601
b4d89997 3602 Section *upgradable = [[[Section alloc] initWithName:@"Available Upgrades" row:0] autorelease];
dc5812ec 3603 Section *section = nil;
b4d89997 3604
36bb2ca2
JF
3605 upgrades_ = 0;
3606 bool unseens = false;
3607
3608 CFLocaleRef locale = CFLocaleCopyCurrent();
3609 CFDateFormatterRef formatter = CFDateFormatterCreate(NULL, locale, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle);
3610
dc5812ec
JF
3611 for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
3612 Package *package = [packages_ objectAtIndex:offset];
dc5812ec 3613
36bb2ca2
JF
3614 if ([package upgradable]) {
3615 ++upgrades_;
3616 [upgradable addToCount];
3617 } else {
3618 unseens = true;
b4d89997 3619 NSDate *seen = [package seen];
dc5812ec 3620
723a0072 3621 NSString *name;
723a0072
JF
3622
3623 if (seen == nil)
36bb2ca2 3624 name = [@"n/a ?" retain];
723a0072 3625 else {
36bb2ca2 3626 name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) seen);
723a0072 3627 }
b4d89997 3628
36bb2ca2 3629 if (section == nil || ![[section name] isEqualToString:name]) {
b4d89997
JF
3630 section = [[[Section alloc] initWithName:name row:offset] autorelease];
3631 [sections_ addObject:section];
3632 }
3633
36bb2ca2
JF
3634 [name release];
3635 [section addToCount];
b4d89997 3636 }
dc5812ec
JF
3637 }
3638
36bb2ca2
JF
3639 CFRelease(formatter);
3640 CFRelease(locale);
b4d89997 3641
36bb2ca2
JF
3642 if (unseens) {
3643 Section *last = [sections_ lastObject];
3644 size_t count = [last count];
3645 [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
3646 [sections_ removeLastObject];
3647 }
dc5812ec 3648
36bb2ca2
JF
3649 if (upgrades_ != 0)
3650 [sections_ insertObject:upgradable atIndex:0];
c25a610d 3651
36bb2ca2
JF
3652 [list_ reloadData];
3653 [self reloadButtons];
3178d79b
JF
3654}
3655
36bb2ca2
JF
3656- (void) resetViewAnimated:(BOOL)animated {
3657 [list_ resetViewAnimated:animated];
3178d79b
JF
3658}
3659
9a7b04c5
JF
3660- (NSString *) leftButtonTitle {
3661 return [(CYBook *)book_ updating] ? nil : @"Refresh";
3662}
3663
36bb2ca2
JF
3664- (NSString *) rightButtonTitle {
3665 return upgrades_ == 0 ? nil : [NSString stringWithFormat:@"Upgrade All (%u)", upgrades_];
3178d79b
JF
3666}
3667
36bb2ca2
JF
3668- (NSString *) title {
3669 return @"Changes";
8fe19fc1
JF
3670}
3671
559dec34
JF
3672@end
3673/* }}} */
3674/* Manage View {{{ */
3675@interface ManageView : PackageTable {
3676}
3677
3678- (id) initWithBook:(RVBook *)book database:(Database *)database;
3679
3680@end
3681
3682@implementation ManageView
3683
3684- (id) initWithBook:(RVBook *)book database:(Database *)database {
3685 if ((self = [super
3686 initWithBook:book
3687 database:database
3688 title:nil
3689 filter:@selector(isInstalledInSection:)
3690 with:nil
3691 ]) != nil) {
3692 } return self;
3693}
3694
3695- (NSString *) title {
3696 return @"Installed Packages";
3697}
3698
3699- (NSString *) backButtonTitle {
3700 return @"All Packages";
3701}
3702
3178d79b
JF
3703@end
3704/* }}} */
36bb2ca2
JF
3705/* Search View {{{ */
3706@protocol SearchViewDelegate
3707- (void) showKeyboard:(BOOL)show;
3708@end
b4d89997 3709
a0be02eb 3710@interface SearchView : RVPage {
36bb2ca2
JF
3711 UIView *accessory_;
3712 UISearchField *field_;
a0be02eb
JF
3713 UITransitionView *transition_;
3714 PackageTable *table_;
3715 UIPreferencesTable *advanced_;
3716 UIView *dimmed_;
3717 bool flipped_;
36bb2ca2 3718}
b4d89997 3719
36bb2ca2
JF
3720- (id) initWithBook:(RVBook *)book database:(Database *)database;
3721- (void) reloadData;
b4d89997 3722
3178d79b
JF
3723@end
3724
36bb2ca2 3725@implementation SearchView
3178d79b 3726
b4d89997 3727- (void) dealloc {
36bb2ca2
JF
3728#ifndef __OBJC2__
3729 [[field_ textTraits] setEditingDelegate:nil];
3730#endif
3731 [field_ setDelegate:nil];
3178d79b 3732
36bb2ca2
JF
3733 [accessory_ release];
3734 [field_ release];
a0be02eb
JF
3735 [transition_ release];
3736 [table_ release];
3737 [advanced_ release];
3738 [dimmed_ release];
36bb2ca2 3739 [super dealloc];
b4d89997
JF
3740}
3741
36bb2ca2
JF
3742- (void) textFieldDidBecomeFirstResponder:(UITextField *)field {
3743 [delegate_ showKeyboard:YES];
a0be02eb
JF
3744 [table_ setEnabled:NO];
3745 [self addSubview:dimmed_];
36bb2ca2 3746}
b4d89997 3747
36bb2ca2 3748- (void) textFieldDidResignFirstResponder:(UITextField *)field {
a0be02eb
JF
3749 [dimmed_ removeFromSuperview];
3750 [table_ setEnabled:YES];
36bb2ca2
JF
3751 [delegate_ showKeyboard:NO];
3752}
b4d89997 3753
36bb2ca2
JF
3754- (void) keyboardInputChanged:(UIFieldEditor *)editor {
3755 NSString *text([field_ text]);
3756 [field_ setClearButtonStyle:(text == nil || [text length] == 0 ? 0 : 2)];
3757}
b4d89997 3758
36bb2ca2
JF
3759- (BOOL) keyboardInput:(id)input shouldInsertText:(NSString *)text isMarkedText:(int)marked {
3760 if ([text length] != 1 || [text characterAtIndex:0] != '\n')
3761 return YES;
b4d89997 3762
36bb2ca2
JF
3763 [self reloadData];
3764 [field_ resignFirstResponder];
3765 return NO;
b4d89997
JF
3766}
3767
36bb2ca2 3768- (id) initWithBook:(RVBook *)book database:(Database *)database {
a0be02eb
JF
3769 if ((self = [super initWithBook:book]) != nil) {
3770 CGRect pageBounds = [book_ pageBounds];
3771
7fea16f2 3772 /*UIImageView *pinstripe = [[[UIImageView alloc] initWithFrame:pageBounds] autorelease];
a0be02eb 3773 [pinstripe setImage:[UIImage applicationImageNamed:@"pinstripe.png"]];
7fea16f2 3774 [self addSubview:pinstripe];*/
a0be02eb
JF
3775
3776 dimmed_ = [[UIView alloc] initWithFrame:pageBounds];
3777 CGColor dimmed(space_, 0, 0, 0, 0.5);
3778 [dimmed_ setBackgroundColor:dimmed];
3779
3780 transition_ = [[UITransitionView alloc] initWithFrame:pageBounds];
3781 [self addSubview:transition_];
3782
3783 advanced_ = [[UIPreferencesTable alloc] initWithFrame:pageBounds];
3784
3785 table_ = [[PackageTable alloc]
3786 initWithBook:book
3787 database:database
3788 title:nil
3789 filter:@selector(isSearchedForBy:)
3790 with:nil
3791 ];
3792
3793 [transition_ transition:0 toView:table_];
3794
559dec34 3795 CGRect cnfrect = {{2, 37}, {17, 18}};
b4d89997 3796
36bb2ca2 3797 CGRect area;
559dec34 3798 area.origin.x = cnfrect.size.width + 17;
36bb2ca2 3799 area.origin.y = 30;
9a7b04c5 3800 area.size.width = [self bounds].size.width - area.origin.x - 18;
36bb2ca2 3801 area.size.height = [UISearchField defaultHeight];
3178d79b 3802
36bb2ca2 3803 field_ = [[UISearchField alloc] initWithFrame:area];
b4d89997 3804
36bb2ca2
JF
3805 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 16);
3806 [field_ setFont:font];
3807 CFRelease(font);
b4d89997 3808
36bb2ca2
JF
3809 [field_ setPlaceholder:@"Package Names & Descriptions"];
3810 [field_ setPaddingTop:5];
3811 [field_ setDelegate:self];
b4d89997 3812
36bb2ca2
JF
3813#ifndef __OBJC2__
3814 UITextTraits *traits = [field_ textTraits];
3815 [traits setEditingDelegate:self];
3816 [traits setReturnKeyType:6];
3817 [traits setAutoCapsType:0];
3818 [traits setAutoCorrectionType:1];
3819#endif
b4d89997 3820
36bb2ca2
JF
3821 UIPushButton *configure = [[[UIPushButton alloc] initWithFrame:cnfrect] autorelease];
3822 [configure setShowPressFeedback:YES];
559dec34 3823 [configure setImage:[UIImage applicationImageNamed:@"advanced.png"]];
36bb2ca2 3824 [configure addTarget:self action:@selector(configurePushed) forEvents:1];
b4d89997 3825
36bb2ca2
JF
3826 accessory_ = [[UIView alloc] initWithFrame:CGRectMake(0, 6, cnfrect.size.width + area.size.width + 6 * 3, area.size.height + 30)];
3827 [accessory_ addSubview:field_];
3828 [accessory_ addSubview:configure];
3829 } return self;
3178d79b
JF
3830}
3831
a0be02eb
JF
3832- (void) flipPage {
3833 LKAnimation *animation = [LKTransition animation];
3834 [animation setType:@"oglFlip"];
3835 [animation setTimingFunction:[LKTimingFunction functionWithName:@"easeInEaseOut"]];
3836 [animation setFillMode:@"extended"];
3837 [animation setTransitionFlags:3];
3838 [animation setDuration:10];
3839 [animation setSpeed:0.35];
7fea16f2
JF
3840 [animation setSubtype:(flipped_ ? @"fromLeft" : @"fromRight")];
3841 [[transition_ _layer] addAnimation:animation forKey:0];
a0be02eb
JF
3842 [transition_ transition:0 toView:(flipped_ ? (UIView *) table_ : (UIView *) advanced_)];
3843 flipped_ = !flipped_;
3844}
3845
36bb2ca2 3846- (void) configurePushed {
a0be02eb
JF
3847 [field_ resignFirstResponder];
3848 [self flipPage];
3849}
3850
3851- (void) resetViewAnimated:(BOOL)animated {
3852 if (flipped_)
3853 [self flipPage];
3854 [table_ resetViewAnimated:animated];
8fe19fc1
JF
3855}
3856
36bb2ca2 3857- (void) reloadData {
a0be02eb
JF
3858 if (flipped_)
3859 [self flipPage];
3860 [table_ setObject:[field_ text]];
3861 [table_ reloadData];
3862 [table_ resetCursor];
3178d79b
JF
3863}
3864
36bb2ca2
JF
3865- (UIView *) accessoryView {
3866 return accessory_;
3867}
b4d89997 3868
a0be02eb
JF
3869- (NSString *) title {
3870 return nil;
3871}
3872
36bb2ca2
JF
3873- (NSString *) backButtonTitle {
3874 return @"Search";
3875}
74127566 3876
3178d79b 3877@end
36bb2ca2 3878/* }}} */
3178d79b 3879
36bb2ca2 3880@implementation CYBook
b4d89997 3881
36bb2ca2
JF
3882- (void) dealloc {
3883 [overlay_ release];
3884 [indicator_ release];
3885 [prompt_ release];
3886 [progress_ release];
3887 [super dealloc];
3888}
b4d89997 3889
9a7b04c5
JF
3890- (BOOL) updating {
3891 return updating_;
3892}
3893
36bb2ca2 3894- (void) update {
9a7b04c5 3895 [navbar_ setPrompt:@""];
36bb2ca2
JF
3896 [navbar_ addSubview:overlay_];
3897 [indicator_ startAnimation];
3898 [prompt_ setText:@"Updating Database..."];
3899 [progress_ setProgress:0];
b4d89997 3900
9a7b04c5
JF
3901 updating_ = true;
3902
36bb2ca2
JF
3903 [NSThread
3904 detachNewThreadSelector:@selector(_update)
3905 toTarget:self
3906 withObject:nil
3907 ];
3178d79b
JF
3908}
3909
36bb2ca2 3910- (void) _update_ {
9a7b04c5
JF
3911 updating_ = false;
3912
36bb2ca2
JF
3913 [overlay_ removeFromSuperview];
3914 [indicator_ stopAnimation];
3915 [delegate_ reloadData];
b4d89997 3916
36bb2ca2 3917 [self setPrompt:[NSString stringWithFormat:@"Last Updated: %@", GetLastUpdate()]];
3178d79b
JF
3918}
3919
36bb2ca2
JF
3920- (id) initWithFrame:(CGRect)frame database:(Database *)database {
3921 if ((self = [super initWithFrame:frame]) != nil) {
3922 database_ = database;
3178d79b 3923
36bb2ca2
JF
3924 CGRect ovrrect = [navbar_ bounds];
3925 ovrrect.size.height = [UINavigationBar defaultSizeWithPrompt].height - [UINavigationBar defaultSize].height;
3178d79b 3926
36bb2ca2 3927 overlay_ = [[UIView alloc] initWithFrame:ovrrect];
b4d89997 3928
36bb2ca2
JF
3929 CGSize indsize = [UIProgressIndicator defaultSizeForStyle:2];
3930 unsigned indoffset = (ovrrect.size.height - indsize.height) / 2;
3931 CGRect indrect = {{indoffset, indoffset}, indsize};
8fe19fc1 3932
36bb2ca2
JF
3933 indicator_ = [[UIProgressIndicator alloc] initWithFrame:indrect];
3934 [indicator_ setStyle:2];
3935 [overlay_ addSubview:indicator_];
b4d89997 3936
36bb2ca2 3937 CGSize prmsize = {200, indsize.width};
b4d89997 3938
36bb2ca2
JF
3939 CGRect prmrect = {{
3940 indoffset * 2 + indsize.width,
3941 (ovrrect.size.height - prmsize.height) / 2
3942 }, prmsize};
b4d89997 3943
36bb2ca2 3944 GSFontRef font = GSFontCreateWithName("Helvetica", kGSFontTraitNone, 12);
b4d89997 3945
36bb2ca2 3946 prompt_ = [[UITextLabel alloc] initWithFrame:prmrect];
b4d89997 3947
36bb2ca2
JF
3948 [prompt_ setColor:White_];
3949 [prompt_ setBackgroundColor:Clear_];
3950 [prompt_ setFont:font];
b4d89997 3951
36bb2ca2 3952 CFRelease(font);
b4d89997 3953
36bb2ca2 3954 [overlay_ addSubview:prompt_];
b4d89997 3955
36bb2ca2 3956 CGSize prgsize = {75, 100};
b4d89997 3957
36bb2ca2
JF
3958 CGRect prgrect = {{
3959 ovrrect.size.width - prgsize.width - 10,
3960 (ovrrect.size.height - prgsize.height) / 2
3961 } , prgsize};
3962
3963 progress_ = [[UIProgressBar alloc] initWithFrame:prgrect];
3964 [progress_ setStyle:0];
3965 [overlay_ addSubview:progress_];
b4d89997 3966 } return self;
3178d79b
JF
3967}
3968
36bb2ca2
JF
3969- (void) _update {
3970 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
dc5812ec 3971
36bb2ca2
JF
3972 Status status;
3973 status.setDelegate(self);
b4d89997 3974
36bb2ca2 3975 [database_ updateWithStatus:status];
3178d79b 3976
36bb2ca2
JF
3977 [self
3978 performSelectorOnMainThread:@selector(_update_)
3979 withObject:nil
3980 waitUntilDone:NO
3981 ];
b4d89997 3982
36bb2ca2 3983 [pool release];
dc5812ec
JF
3984}
3985
36bb2ca2
JF
3986- (void) setProgressError:(NSString *)error {
3987 [self
3988 performSelectorOnMainThread:@selector(_setProgressError:)
3989 withObject:error
3990 waitUntilDone:YES
3991 ];
dc5812ec
JF
3992}
3993
36bb2ca2
JF
3994- (void) setProgressTitle:(NSString *)title {
3995 [self
3996 performSelectorOnMainThread:@selector(_setProgressTitle:)
3997 withObject:title
3998 waitUntilDone:YES
3999 ];
3178d79b
JF
4000}
4001
36bb2ca2
JF
4002- (void) setProgressPercent:(float)percent {
4003}
b4d89997 4004
36bb2ca2
JF
4005- (void) addProgressOutput:(NSString *)output {
4006 [self
4007 performSelectorOnMainThread:@selector(_addProgressOutput:)
4008 withObject:output
4009 waitUntilDone:YES
4010 ];
4011}
b4d89997 4012
36bb2ca2
JF
4013- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4014 [sheet dismiss];
4015}
4016
4017- (void) _setProgressError:(NSString *)error {
4018 [prompt_ setText:[NSString stringWithFormat:@"Error: %@", error]];
4019}
4020
4021- (void) _setProgressTitle:(NSString *)title {
4022 [prompt_ setText:[title stringByAppendingString:@"..."]];
4023}
b4d89997 4024
36bb2ca2 4025- (void) _addProgressOutput:(NSString *)output {
8fe19fc1
JF
4026}
4027
3178d79b 4028@end
3178d79b 4029
2367a917 4030@interface Cydia : UIApplication <
3178d79b 4031 ConfirmationViewDelegate,
b4d89997 4032 ProgressViewDelegate,
5f6bff8c
JF
4033 SearchViewDelegate,
4034 CydiaDelegate
2367a917 4035> {
dc5812ec 4036 UIWindow *window_;
36bb2ca2 4037
3178d79b
JF
4038 UIView *underlay_;
4039 UIView *overlay_;
36bb2ca2 4040 CYBook *book_;
2367a917 4041 UIButtonBar *buttonbar_;
3178d79b 4042
3178d79b 4043 ConfirmationView *confirm_;
dc5812ec
JF
4044
4045 Database *database_;
4046 ProgressView *progress_;
4047
06aa974d 4048 unsigned tag_;
b4d89997
JF
4049
4050 UIKeyboard *keyboard_;
dc5812ec
JF
4051}
4052
dc5812ec
JF
4053@end
4054
4055@implementation Cydia
4056
36bb2ca2
JF
4057- (void) _reloadData {
4058 /*UIProgressHUD *hud = [[UIProgressHUD alloc] initWithWindow:window_];
4059 [hud setText:@"Reloading Data"];
4060 [overlay_ addSubview:hud];
4061 [hud show:YES];*/
dc5812ec 4062
dc5812ec 4063 [database_ reloadData];
c25a610d 4064
b4d89997
JF
4065 size_t count = 16;
4066
4067 if (Packages_ == nil) {
4068 Packages_ = [[NSMutableDictionary alloc] initWithCapacity:count];
4069 [Metadata_ setObject:Packages_ forKey:@"Packages"];
4070 }
4071
36bb2ca2 4072 size_t changes(0);
b4d89997 4073
36bb2ca2
JF
4074 NSArray *packages = [database_ packages];
4075 for (int i(0), e([packages count]); i != e; ++i) {
4076 Package *package = [packages objectAtIndex:i];
4077 if ([package upgradable])
4078 ++changes;
4079 }
b4d89997 4080
36bb2ca2
JF
4081 if (changes != 0) {
4082 NSString *badge([[NSNumber numberWithInt:changes] stringValue]);
c25a610d 4083 [buttonbar_ setBadgeValue:badge forButton:3];
9e98e020
JF
4084 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4085 [buttonbar_ setBadgeAnimated:YES forButton:3];
c25a610d
JF
4086 [self setApplicationBadge:badge];
4087 } else {
4088 [buttonbar_ setBadgeValue:nil forButton:3];
9e98e020
JF
4089 if ([buttonbar_ respondsToSelector:@selector(setBadgeAnimated:forButton:)])
4090 [buttonbar_ setBadgeAnimated:NO forButton:3];
c25a610d
JF
4091 [self removeApplicationBadge];
4092 }
b4d89997
JF
4093
4094 _assert([Metadata_ writeToFile:@"/var/lib/cydia/metadata.plist" atomically:YES] == YES);
36bb2ca2
JF
4095
4096 [book_ reloadData];
4097 /*[hud show:NO];
4098 [hud removeFromSuperview];*/
b4d89997
JF
4099}
4100
36bb2ca2
JF
4101- (void) reloadData {
4102 @synchronized (self) {
4103 if (confirm_ == nil)
4104 [self _reloadData];
4105 }
b4d89997
JF
4106}
4107
4108- (void) resolve {
4109 pkgProblemResolver *resolver = [database_ resolver];
4110
4111 resolver->InstallProtect();
4112 if (!resolver->Resolve(true))
4113 _error->Discard();
2367a917
JF
4114}
4115
4116- (void) perform {
8fe19fc1 4117 [database_ prepare];
49048579
JF
4118
4119 if ([database_ cache]->BrokenCount() == 0)
4120 confirm_ = [[ConfirmationView alloc] initWithView:underlay_ database:database_ delegate:self];
4121 else {
4122 NSMutableArray *broken = [NSMutableArray arrayWithCapacity:16];
4123 NSArray *packages = [database_ packages];
4124
4125 for (size_t i(0); i != [packages count]; ++i) {
4126 Package *package = [packages objectAtIndex:i];
4127 if ([package broken])
4128 [broken addObject:[package name]];
4129 }
4130
4131 UIAlertSheet *sheet = [[[UIAlertSheet alloc]
4132 initWithTitle:[NSString stringWithFormat:@"%d Broken Packages", [database_ cache]->BrokenCount()]
4133 buttons:[NSArray arrayWithObjects:@"Okay", nil]
4134 defaultButtonIndex:0
4135 delegate:self
4136 context:self
4137 ] autorelease];
4138
4139 [sheet setBodyText:[NSString stringWithFormat:@"The following packages have unmet dependencies:\n\n%@", [broken componentsJoinedByString:@"\n"]]];
4140 [sheet popupAlertAnimated:YES];
4141
36bb2ca2 4142 [self _reloadData];
49048579 4143 }
3178d79b
JF
4144}
4145
36bb2ca2
JF
4146- (void) installPackage:(Package *)package {
4147 @synchronized (self) {
4148 [package install];
4149 [self resolve];
4150 [self perform];
4151 }
4152}
4153
4154- (void) removePackage:(Package *)package {
4155 @synchronized (self) {
4156 [package remove];
4157 [self resolve];
4158 [self perform];
4159 }
4160}
4161
4162- (void) distUpgrade {
4163 @synchronized (self) {
4164 [database_ upgrade];
4165 [self perform];
4166 }
b4d89997
JF
4167}
4168
3178d79b 4169- (void) cancel {
36bb2ca2
JF
4170 @synchronized (self) {
4171 [confirm_ release];
4172 confirm_ = nil;
4173 [self _reloadData];
4174 }
3178d79b
JF
4175}
4176
4177- (void) confirm {
4178 [overlay_ removeFromSuperview];
b4d89997 4179 restart_ = true;
3178d79b 4180
2367a917
JF
4181 [progress_
4182 detachNewThreadSelector:@selector(perform)
4183 toTarget:database_
4184 withObject:nil
b4d89997 4185 title:@"Running..."
2367a917
JF
4186 ];
4187}
4188
b31b87cc
JF
4189- (void) bootstrap_ {
4190 [database_ update];
4191 [database_ upgrade];
4192 [database_ prepare];
4193 [database_ perform];
4194}
4195
4196- (void) bootstrap {
4197 [progress_
4198 detachNewThreadSelector:@selector(bootstrap_)
4199 toTarget:self
4200 withObject:nil
4201 title:@"Bootstrap Install..."
4202 ];
4203}
4204
2367a917 4205- (void) progressViewIsComplete:(ProgressView *)progress {
36bb2ca2
JF
4206 @synchronized (self) {
4207 [self _reloadData];
4208
4209 if (confirm_ != nil) {
4210 [underlay_ addSubview:overlay_];
4211 [confirm_ removeFromSuperview];
4212 [confirm_ release];
4213 confirm_ = nil;
4214 }
3178d79b 4215 }
dc5812ec
JF
4216}
4217
36bb2ca2
JF
4218- (void) alertSheet:(UIAlertSheet *)sheet buttonClicked:(int)button {
4219 [sheet dismiss];
4220}
2367a917 4221
36bb2ca2
JF
4222- (void) setPage:(RVPage *)page {
4223 [page setDelegate:self];
4224 [book_ setPage:page];
dc5812ec
JF
4225}
4226
36bb2ca2
JF
4227- (RVPage *) _setNewsPage {
4228 BrowserView *browser = [[[BrowserView alloc] initWithBook:book_ database:database_] autorelease];
4229 [self setPage:browser];
4230 [browser loadURL:[NSURL URLWithString:@"http://cydia.saurik.com/"]];
4231 return browser;
2367a917
JF
4232}
4233
dc5812ec 4234- (void) buttonBarItemTapped:(id)sender {
06aa974d 4235 unsigned tag = [sender tag];
559dec34
JF
4236 if (tag == tag_) {
4237 [book_ resetViewAnimated:YES];
4238 return;
4239 }
dc5812ec 4240
06aa974d 4241 switch (tag) {
36bb2ca2
JF
4242 case 1:
4243 [self _setNewsPage];
4244 break;
4245
4246 case 2:
4247 [self setPage:[[[InstallView alloc] initWithBook:book_ database:database_] autorelease]];
4248 break;
4249
4250 case 3:
4251 [self setPage:[[[ChangesView alloc] initWithBook:book_ database:database_] autorelease]];
4252 break;
4253
4254 case 4:
559dec34 4255 [self setPage:[[[ManageView alloc] initWithBook:book_ database:database_] autorelease]];
36bb2ca2
JF
4256 break;
4257
4258 case 5:
4259 [self setPage:[[[SearchView alloc] initWithBook:book_ database:database_] autorelease]];
4260 break;
dc5812ec
JF
4261
4262 default:
4263 _assert(false);
4264 }
4265
06aa974d 4266 tag_ = tag;
06aa974d
JF
4267}
4268
b4d89997 4269- (void) applicationWillSuspend {
36bb2ca2
JF
4270 [super applicationWillSuspend];
4271
b4d89997 4272 if (restart_)
ec97ef06
JF
4273 if (FW_LEAST(1,1,3))
4274 notify_post("com.apple.language.changed");
4275 else
4276 system("launchctl stop com.apple.SpringBoard");
b4d89997
JF
4277}
4278
dc5812ec
JF
4279- (void) applicationDidFinishLaunching:(id)unused {
4280 _assert(pkgInitConfig(*_config));
4281 _assert(pkgInitSystem(*_config, _system));
4282
3178d79b 4283 confirm_ = nil;
06aa974d 4284 tag_ = 1;
3178d79b 4285
dc5812ec
JF
4286 CGRect screenrect = [UIHardware fullScreenApplicationContentRect];
4287 window_ = [[UIWindow alloc] initWithContentRect:screenrect];
4288
4289 [window_ orderFront: self];
4290 [window_ makeKey: self];
4291 [window_ _setHidden: NO];
4292
2367a917 4293 progress_ = [[ProgressView alloc] initWithFrame:[window_ bounds] delegate:self];
dc5812ec
JF
4294 [window_ setContentView:progress_];
4295
3178d79b
JF
4296 underlay_ = [[UIView alloc] initWithFrame:[progress_ bounds]];
4297 [progress_ setContentView:underlay_];
4298
4299 overlay_ = [[UIView alloc] initWithFrame:[underlay_ bounds]];
b31b87cc
JF
4300
4301 if (!bootstrap_)
4302 [underlay_ addSubview:overlay_];
dc5812ec 4303
36bb2ca2
JF
4304 database_ = [[Database alloc] init];
4305 [database_ setDelegate:progress_];
06aa974d 4306
36bb2ca2
JF
4307 book_ = [[CYBook alloc] initWithFrame:CGRectMake(
4308 0, 0, screenrect.size.width, screenrect.size.height - 48
4309 ) database:database_];
dc5812ec 4310
36bb2ca2 4311 [book_ setDelegate:self];
dc5812ec 4312
36bb2ca2 4313 [overlay_ addSubview:book_];
b19871dd 4314
dc5812ec
JF
4315 NSArray *buttonitems = [NSArray arrayWithObjects:
4316 [NSDictionary dictionaryWithObjectsAndKeys:
4317 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
36bb2ca2
JF
4318 @"news-up.png", kUIButtonBarButtonInfo,
4319 @"news-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
4320 [NSNumber numberWithInt:1], kUIButtonBarButtonTag,
4321 self, kUIButtonBarButtonTarget,
36bb2ca2 4322 @"News", kUIButtonBarButtonTitle,
dc5812ec
JF
4323 @"0", kUIButtonBarButtonType,
4324 nil],
4325
4326 [NSDictionary dictionaryWithObjectsAndKeys:
4327 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
4328 @"install-up.png", kUIButtonBarButtonInfo,
4329 @"install-dn.png", kUIButtonBarButtonSelectedInfo,
4330 [NSNumber numberWithInt:2], kUIButtonBarButtonTag,
4331 self, kUIButtonBarButtonTarget,
4332 @"Install", kUIButtonBarButtonTitle,
4333 @"0", kUIButtonBarButtonType,
4334 nil],
4335
4336 [NSDictionary dictionaryWithObjectsAndKeys:
4337 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
4338 @"changes-up.png", kUIButtonBarButtonInfo,
4339 @"changes-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
4340 [NSNumber numberWithInt:3], kUIButtonBarButtonTag,
4341 self, kUIButtonBarButtonTarget,
b4d89997 4342 @"Changes", kUIButtonBarButtonTitle,
dc5812ec
JF
4343 @"0", kUIButtonBarButtonType,
4344 nil],
4345
4346 [NSDictionary dictionaryWithObjectsAndKeys:
4347 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
4348 @"manage-up.png", kUIButtonBarButtonInfo,
4349 @"manage-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
4350 [NSNumber numberWithInt:4], kUIButtonBarButtonTag,
4351 self, kUIButtonBarButtonTarget,
36bb2ca2 4352 @"Manage", kUIButtonBarButtonTitle,
dc5812ec
JF
4353 @"0", kUIButtonBarButtonType,
4354 nil],
4355
4356 [NSDictionary dictionaryWithObjectsAndKeys:
4357 @"buttonBarItemTapped:", kUIButtonBarButtonAction,
b4d89997
JF
4358 @"search-up.png", kUIButtonBarButtonInfo,
4359 @"search-dn.png", kUIButtonBarButtonSelectedInfo,
dc5812ec
JF
4360 [NSNumber numberWithInt:5], kUIButtonBarButtonTag,
4361 self, kUIButtonBarButtonTarget,
b4d89997 4362 @"Search", kUIButtonBarButtonTitle,
dc5812ec
JF
4363 @"0", kUIButtonBarButtonType,
4364 nil],
4365 nil];
4366
2367a917 4367 buttonbar_ = [[UIButtonBar alloc]
3178d79b 4368 initInView:overlay_
dc5812ec
JF
4369 withFrame:CGRectMake(
4370 0, screenrect.size.height - 48,
4371 screenrect.size.width, 48
4372 )
4373 withItemList:buttonitems
4374 ];
4375
2367a917
JF
4376 [buttonbar_ setDelegate:self];
4377 [buttonbar_ setBarStyle:1];
4378 [buttonbar_ setButtonBarTrackingMode:2];
dc5812ec
JF
4379
4380 int buttons[5] = {1, 2, 3, 4, 5};
2367a917
JF
4381 [buttonbar_ registerButtonGroup:0 withButtons:buttons withCount:5];
4382 [buttonbar_ showButtonGroup:0 withDuration:0];
dc5812ec
JF
4383
4384 for (int i = 0; i != 5; ++i)
2367a917 4385 [[buttonbar_ viewWithTag:(i + 1)] setFrame:CGRectMake(
dc5812ec
JF
4386 i * 64 + 2, 1, 60, 48
4387 )];
4388
2367a917 4389 [buttonbar_ showSelectionForButton:1];
3178d79b 4390 [overlay_ addSubview:buttonbar_];
dc5812ec 4391
b4d89997 4392 [UIKeyboard initImplementationNow];
b4d89997
JF
4393 CGSize keysize = [UIKeyboard defaultSize];
4394 CGRect keyrect = {{0, [overlay_ bounds].size.height - keysize.height}, keysize};
4395 keyboard_ = [[UIKeyboard alloc] initWithFrame:keyrect];
d73cede2 4396 [[UIKeyboardImpl sharedInstance] setSoundsEnabled:(Sounds_Keyboard_ ? YES : NO)];
b4d89997 4397
36bb2ca2
JF
4398 [self reloadData];
4399 [book_ update];
b31b87cc
JF
4400
4401 [progress_ resetView];
4402
4403 if (bootstrap_)
4404 [self bootstrap];
36bb2ca2
JF
4405 else
4406 [self _setNewsPage];
b4d89997
JF
4407}
4408
4409- (void) showKeyboard:(BOOL)show {
4410 if (show)
4411 [overlay_ addSubview:keyboard_];
4412 else
4413 [keyboard_ removeFromSuperview];
dc5812ec
JF
4414}
4415
36bb2ca2
JF
4416- (void) slideUp:(UIAlertSheet *)alert {
4417 [alert presentSheetFromButtonBar:buttonbar_];
4418}
4419
dc5812ec
JF
4420@end
4421
b4d89997
JF
4422void AddPreferences(NSString *plist) {
4423 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4424
4425 NSMutableDictionary *settings = [[[NSMutableDictionary alloc] initWithContentsOfFile:plist] autorelease];
4426 _assert(settings != NULL);
4427 NSMutableArray *items = [settings objectForKey:@"items"];
4428
4429 bool cydia(false);
4430
4431 for (size_t i(0); i != [items count]; ++i) {
4432 NSMutableDictionary *item([items objectAtIndex:i]);
4433 NSString *label = [item objectForKey:@"label"];
4434 if (label != nil && [label isEqualToString:@"Cydia"]) {
4435 cydia = true;
4436 break;
4437 }
4438 }
4439
4440 if (!cydia) {
4441 for (size_t i(0); i != [items count]; ++i) {
4442 NSDictionary *item([items objectAtIndex:i]);
4443 NSString *label = [item objectForKey:@"label"];
4444 if (label != nil && [label isEqualToString:@"General"]) {
4445 [items insertObject:[NSDictionary dictionaryWithObjectsAndKeys:
4446 @"CydiaSettings", @"bundle",
4447 @"PSLinkCell", @"cell",
4448 [NSNumber numberWithBool:YES], @"hasIcon",
4449 [NSNumber numberWithBool:YES], @"isController",
4450 @"Cydia", @"label",
4451 nil] atIndex:(i + 1)];
4452
4453 break;
4454 }
4455 }
4456
4457 _assert([settings writeToFile:plist atomically:YES] == YES);
4458 }
4459
4460 [pool release];
4461}
4462
4463/*IMP alloc_;
4464id Alloc_(id self, SEL selector) {
4465 id object = alloc_(self, selector);
4466 fprintf(stderr, "[%s]A-%p\n", self->isa->name, object);
4467 return object;
4468}*/
4469
36bb2ca2
JF
4470/*IMP dealloc_;
4471id Dealloc_(id self, SEL selector) {
4472 id object = dealloc_(self, selector);
4473 fprintf(stderr, "[%s]D-%p\n", self->isa->name, object);
4474 return object;
4475}*/
b4d89997 4476
36bb2ca2 4477int main(int argc, char *argv[]) {
d73cede2
JF
4478 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
4479
b31b87cc
JF
4480 bootstrap_ = argc > 1 && strcmp(argv[1], "--bootstrap") == 0;
4481
d73cede2
JF
4482 Home_ = NSHomeDirectory();
4483
4484 {
4485 NSString *plist = [Home_ stringByAppendingString:@"/Library/Preferences/com.apple.preferences.sounds.plist"];
4486 if (NSDictionary *sounds = [NSDictionary dictionaryWithContentsOfFile:plist])
4487 if (NSNumber *keyboard = [sounds objectForKey:@"keyboard"])
4488 Sounds_Keyboard_ = [keyboard boolValue];
4489 }
4490
b4d89997
JF
4491 setuid(0);
4492 setgid(0);
4493
4494 /*Method alloc = class_getClassMethod([NSObject class], @selector(alloc));
4495 alloc_ = alloc->method_imp;
4496 alloc->method_imp = (IMP) &Alloc_;*/
4497
36bb2ca2
JF
4498 /*Method dealloc = class_getClassMethod([NSObject class], @selector(dealloc));
4499 dealloc_ = dealloc->method_imp;
4500 dealloc->method_imp = (IMP) &Dealloc_;*/
4501
2a8d9add
JF
4502 if (NSDictionary *sysver = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]) {
4503 if (NSString *prover = [sysver valueForKey:@"ProductVersion"]) {
5f6bff8c 4504 Firmware_ = strdup([prover UTF8String]);
2a8d9add
JF
4505 NSArray *versions = [prover componentsSeparatedByString:@"."];
4506 int count = [versions count];
4507 Major_ = count > 0 ? [[versions objectAtIndex:0] intValue] : 0;
51a1ffdf
JF
4508 Minor_ = count > 1 ? [[versions objectAtIndex:1] intValue] : 0;
4509 BugFix_ = count > 2 ? [[versions objectAtIndex:2] intValue] : 0;
2a8d9add
JF
4510 }
4511 }
4512
3178d79b
JF
4513 size_t size;
4514 sysctlbyname("hw.machine", NULL, &size, NULL, 0);
4515 char *machine = new char[size];
4516 sysctlbyname("hw.machine", machine, &size, NULL, 0);
4517 Machine_ = machine;
4518
4519 if (CFMutableDictionaryRef dict = IOServiceMatching("IOPlatformExpertDevice"))
4520 if (io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, dict)) {
4521 if (CFTypeRef serial = IORegistryEntryCreateCFProperty(service, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0)) {
4522 SerialNumber_ = strdup(CFStringGetCStringPtr((CFStringRef) serial, CFStringGetSystemEncoding()));
4523 CFRelease(serial);
4524 }
4525
4526 IOObjectRelease(service);
4527 }
4528
99364ec4
JF
4529 /*AddPreferences(@"/Applications/Preferences.app/Settings-iPhone.plist");
4530 AddPreferences(@"/Applications/Preferences.app/Settings-iPod.plist");*/
b4d89997
JF
4531
4532 if ((Metadata_ = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/lib/cydia/metadata.plist"]) == NULL)
4533 Metadata_ = [[NSMutableDictionary alloc] initWithCapacity:2];
4534 else
4535 Packages_ = [Metadata_ objectForKey:@"Packages"];
4536
68a238ec 4537 setenv("CYDIA", "", _not(int));
26c2dd8c
JF
4538 if (access("/User", F_OK) != 0)
4539 system("/usr/libexec/cydia/firmware.sh");
9e98e020
JF
4540 system("dpkg --configure -a");
4541
36bb2ca2
JF
4542 space_ = CGColorSpaceCreateDeviceRGB();
4543
4544 Black_.Set(space_, 0.0, 0.0, 0.0, 1.0);
4545 Clear_.Set(space_, 0.0, 0.0, 0.0, 0.0);
4546 Red_.Set(space_, 1.0, 0.0, 0.0, 1.0);
4547 White_.Set(space_, 1.0, 1.0, 1.0, 1.0);
4548
4549 int value = UIApplicationMain(argc, argv, [Cydia class]);
4550
4551 CGColorSpaceRelease(space_);
4552
dc5812ec 4553 [pool release];
36bb2ca2 4554 return value;
dc5812ec 4555}