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