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