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