#include <IOKit/IOKitLib.h>
#include <QuartzCore/CALayer.h>
+#include <QuartzCore/CAAnimation.h>
+#include <QuartzCore/CAMediaTimingFunction.h>
#include <WebCore/WebCoreThread.h>
#define SpringBoard_ "/System/Library/LaunchDaemons/com.apple.SpringBoard.plist"
#define NotifyConfig_ "/etc/notify.conf"
-static bool Queuing_;
-
static CYColor Blue_;
static CYColor Blueish_;
static CYColor Black_;
@end
// }}}
-/* Confirmation Controller {{{ */
-bool DepSubstrate(const pkgCache::VerIterator &iterator) {
- if (!iterator.end())
- for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
- if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
- continue;
- pkgCache::PkgIterator package(dep.TargetPkg());
- if (package.end())
- continue;
- if (strcmp(package.Name(), "mobilesubstrate") == 0)
- return true;
- }
-
- return false;
+/* Package Cell {{{ */
+@interface PackageCell : CyteTableViewCell <
+ CyteTableViewCellDelegate
+> {
+ _H<UIImage> icon_;
+ _H<NSString> name_;
+ _H<NSString> description_;
+ bool commercial_;
+ _H<NSString> source_;
+ _H<UIImage> badge_;
+ _H<UIImage> placard_;
}
-@protocol ConfirmationControllerDelegate
-- (void) cancelAndClear:(bool)clear;
-- (void) confirmWithNavigationController:(UINavigationController *)navigation;
-- (void) queue;
-@end
-
-@interface ConfirmationController : CydiaWebViewController {
- _transient Database *database_;
-
- _H<UIAlertView> essential_;
-
- _H<NSDictionary> changes_;
- _H<NSMutableArray> issues_;
- _H<NSDictionary> sizes_;
-
- BOOL substrate_;
-}
+- (PackageCell *) init;
+- (void) setPackage:(Package *)package;
-- (id) initWithDatabase:(Database *)database;
+- (void) drawContentRect:(CGRect)rect;
@end
-@implementation ConfirmationController
-
-- (void) complete {
- if (substrate_)
- RestartSubstrate_ = true;
- [delegate_ confirmWithNavigationController:[self navigationController]];
-}
+@implementation PackageCell
-- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
- NSString *context([alert context]);
+- (PackageCell *) init {
+ CGRect frame(CGRectMake(0, 0, 320, 74));
+ if ((self = [super initWithFrame:frame reuseIdentifier:@"Package"]) != nil) {
+ UIView *content([self contentView]);
+ CGRect bounds([content bounds]);
- if ([context isEqualToString:@"remove"]) {
- if (button == [alert cancelButtonIndex])
- [self dismissModalViewControllerAnimated:YES];
- else if (button == [alert firstOtherButtonIndex]) {
- [self performSelector:@selector(complete) withObject:nil afterDelay:0];
- }
+ content_ = [[[CyteTableViewCellContentView alloc] initWithFrame:bounds] autorelease];
+ [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [content addSubview:content_];
- [alert dismissWithClickedButtonIndex:-1 animated:YES];
- } else if ([context isEqualToString:@"unable"]) {
- [self dismissModalViewControllerAnimated:YES];
- [alert dismissWithClickedButtonIndex:-1 animated:YES];
- } else {
- [super alertView:alert clickedButtonAtIndex:button];
- }
+ [content_ setDelegate:self];
+ [content_ setOpaque:YES];
+ } return self;
}
-- (void) _doContinue {
- [delegate_ cancelAndClear:NO];
- [self dismissModalViewControllerAnimated:YES];
+- (NSString *) accessibilityLabel {
+ return [NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), (id) name_, (id) description_];
}
-- (id) invokeDefaultMethodWithArguments:(NSArray *)args {
- [self performSelectorOnMainThread:@selector(_doContinue) withObject:nil waitUntilDone:NO];
- return nil;
-}
+- (void) setPackage:(Package *)package {
+ icon_ = nil;
+ name_ = nil;
+ description_ = nil;
+ source_ = nil;
+ badge_ = nil;
+ placard_ = nil;
-- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
- [super webView:view didClearWindowObject:window forFrame:frame];
+ if (package == nil)
+ [content_ setBackgroundColor:[UIColor whiteColor]];
+ else {
+ [package parse];
- [window setValue:[[NSDictionary dictionaryWithObjectsAndKeys:
- (id) changes_, @"changes",
- (id) issues_, @"issues",
- (id) sizes_, @"sizes",
- self, @"queue",
- nil] Cydia$webScriptObjectInContext:window] forKey:@"cydiaConfirm"];
-}
+ Source *source = [package source];
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super init]) != nil) {
- database_ = database;
+ icon_ = [package icon];
- NSMutableArray *installs([NSMutableArray arrayWithCapacity:16]);
- NSMutableArray *reinstalls([NSMutableArray arrayWithCapacity:16]);
- NSMutableArray *upgrades([NSMutableArray arrayWithCapacity:16]);
- NSMutableArray *downgrades([NSMutableArray arrayWithCapacity:16]);
- NSMutableArray *removes([NSMutableArray arrayWithCapacity:16]);
+ if (NSString *name = [package name])
+ name_ = [NSString stringWithString:name];
- bool remove(false);
+ NSString *description(nil);
- pkgCacheFile &cache([database_ cache]);
- NSArray *packages([database_ packages]);
- pkgDepCache::Policy *policy([database_ policy]);
+ if (description == nil && IsWildcat_)
+ description = [package longDescription];
+ if (description == nil)
+ description = [package shortDescription];
- issues_ = [NSMutableArray arrayWithCapacity:4];
+ if (description != nil)
+ description_ = [NSString stringWithString:description];
- for (Package *package in packages) {
- pkgCache::PkgIterator iterator([package iterator]);
- NSString *name([package id]);
+ commercial_ = [package isCommercial];
- if ([package broken]) {
- NSMutableArray *reasons([NSMutableArray arrayWithCapacity:4]);
+ NSString *label = nil;
+ bool trusted = false;
- [issues_ addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- name, @"package",
- reasons, @"reasons",
- nil]];
+ if (source != nil) {
+ label = [source label];
+ trusted = [source trusted];
+ } else if ([[package id] isEqualToString:@"firmware"])
+ label = UCLocalize("APPLE");
+ else
+ label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")];
- pkgCache::VerIterator ver(cache[iterator].InstVerIter(cache));
- if (ver.end())
- continue;
+ NSString *from(label);
- for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
- pkgCache::DepIterator start;
- pkgCache::DepIterator end;
- dep.GlobOr(start, end); // ++dep
+ NSString *section = [package simpleSection];
+ if (section != nil && ![section isEqualToString:label]) {
+ section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
+ from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section];
+ }
- if (!cache->IsImportantDep(end))
- continue;
- if ((cache[end] & pkgDepCache::DepGInstall) != 0)
- continue;
+ source_ = [NSString stringWithFormat:UCLocalize("FROM"), from];
- NSMutableArray *clauses([NSMutableArray arrayWithCapacity:4]);
+ if (NSString *purpose = [package primaryPurpose])
+ badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]];
- [reasons addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSString stringWithUTF8String:start.DepType()], @"relationship",
- clauses, @"clauses",
- nil]];
+ UIColor *color;
+ NSString *placard;
- _forever {
- NSString *reason, *installed((NSString *) [WebUndefined undefined]);
-
- pkgCache::PkgIterator target(start.TargetPkg());
- if (target->ProvidesList != 0)
- reason = @"missing";
- else {
- pkgCache::VerIterator ver(cache[target].InstVerIter(cache));
- if (!ver.end()) {
- reason = @"installed";
- installed = [NSString stringWithUTF8String:ver.VerStr()];
- } else if (!cache[target].CandidateVerIter(cache).end())
- reason = @"uninstalled";
- else if (target->ProvidesList == 0)
- reason = @"uninstallable";
- else
- reason = @"virtual";
- }
-
- NSDictionary *version(start.TargetVer() == 0 ? [NSNull null] : [NSDictionary dictionaryWithObjectsAndKeys:
- [NSString stringWithUTF8String:start.CompType()], @"operator",
- [NSString stringWithUTF8String:start.TargetVer()], @"value",
- nil]);
-
- [clauses addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSString stringWithUTF8String:start.TargetPkg().Name()], @"package",
- version, @"version",
- reason, @"reason",
- installed, @"installed",
- nil]];
-
- // yes, seriously. (wtf?)
- if (start == end)
- break;
- ++start;
- }
- }
+ if (NSString *mode = [package mode]) {
+ if ([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]) {
+ color = RemovingColor_;
+ //placard = @"removing";
+ } else {
+ color = InstallingColor_;
+ //placard = @"installing";
}
- pkgDepCache::StateCache &state(cache[iterator]);
-
- static Pcre special_r("^(firmware$|gsc\\.|cy\\+)");
-
- if (state.NewInstall())
- [installs addObject:name];
- // XXX: else if (state.Install())
- else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
- [reinstalls addObject:name];
- // XXX: move before previous if
- else if (state.Upgrade())
- [upgrades addObject:name];
- else if (state.Downgrade())
- [downgrades addObject:name];
- else if (!state.Delete())
- // XXX: _assert(state.Keep());
- continue;
- else if (special_r(name))
- [issues_ addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSNull null], @"package",
- [NSArray arrayWithObjects:
- [NSDictionary dictionaryWithObjectsAndKeys:
- @"Conflicts", @"relationship",
- [NSArray arrayWithObjects:
- [NSDictionary dictionaryWithObjectsAndKeys:
- name, @"package",
- [NSNull null], @"version",
- @"installed", @"reason",
- nil],
- nil], @"clauses",
- nil],
- nil], @"reasons",
- nil]];
- else {
- if ([package essential])
- remove = true;
- [removes addObject:name];
- }
+ // XXX: the removing/installing placards are not @2x
+ placard = nil;
+ } else {
+ color = [UIColor whiteColor];
- substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
- substrate_ |= DepSubstrate(iterator.CurrentVer());
+ if ([package installed] != nil)
+ placard = @"installed";
+ else
+ placard = nil;
}
- if (!remove)
- essential_ = nil;
- else if (Advanced_) {
- NSString *parenthetical(UCLocalize("PARENTHETICAL"));
+ [content_ setBackgroundColor:color];
- essential_ = [[[UIAlertView alloc]
- initWithTitle:UCLocalize("REMOVING_ESSENTIALS")
- message:UCLocalize("REMOVING_ESSENTIALS_EX")
- delegate:self
- cancelButtonTitle:[NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")]
- otherButtonTitles:
- [NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")],
- nil
- ] autorelease];
+ if (placard != nil)
+ placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/%@.png", App_, placard]];
+ }
- [essential_ setContext:@"remove"];
- [essential_ setNumberOfRows:2];
- } else {
- essential_ = [[[UIAlertView alloc]
- initWithTitle:UCLocalize("UNABLE_TO_COMPLY")
- message:UCLocalize("UNABLE_TO_COMPLY_EX")
- delegate:self
- cancelButtonTitle:UCLocalize("OKAY")
- otherButtonTitles:nil
- ] autorelease];
+ [self setNeedsDisplay];
+ [content_ setNeedsDisplay];
+}
+
+- (void) drawContentRect:(CGRect)rect {
+ bool highlighted(highlighted_);
+ float width([self bounds].size.width);
- [essential_ setContext:@"unable"];
+ if (icon_ != nil) {
+ CGRect rect;
+ rect.size = [(UIImage *) icon_ size];
+
+ while (rect.size.width > 32 || rect.size.height > 32) {
+ rect.size.width /= 2;
+ rect.size.height /= 2;
}
- changes_ = [NSDictionary dictionaryWithObjectsAndKeys:
- installs, @"installs",
- reinstalls, @"reinstalls",
- upgrades, @"upgrades",
- downgrades, @"downgrades",
- removes, @"removes",
- nil];
+ rect.origin.x = 25 - rect.size.width / 2;
+ rect.origin.y = 25 - rect.size.height / 2;
- sizes_ = [NSDictionary dictionaryWithObjectsAndKeys:
- [NSNumber numberWithInteger:[database_ fetcher].FetchNeeded()], @"downloading",
- [NSNumber numberWithInteger:[database_ fetcher].PartialPresent()], @"resuming",
- nil];
+ [icon_ drawInRect:rect];
+ }
- [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/confirm/", UI_]]];
- } return self;
-}
+ if (badge_ != nil) {
+ CGRect rect;
+ rect.size = [(UIImage *) badge_ size];
-- (UIBarButtonItem *) leftButton {
- return [[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("CANCEL")
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(cancelButtonClicked)
- ] autorelease];
-}
+ rect.size.width /= 2;
+ rect.size.height /= 2;
-#if !AlwaysReload
-- (void) applyRightButton {
- if ([issues_ count] == 0 && ![self isLoading])
- [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("CONFIRM")
- style:UIBarButtonItemStyleDone
- target:self
- action:@selector(confirmButtonClicked)
- ] autorelease]];
- else
- [[self navigationItem] setRightBarButtonItem:nil];
-}
-#endif
+ rect.origin.x = 36 - rect.size.width / 2;
+ rect.origin.y = 36 - rect.size.height / 2;
-- (void) cancelButtonClicked {
- [delegate_ cancelAndClear:YES];
- [self dismissModalViewControllerAnimated:YES];
-}
+ [badge_ drawInRect:rect];
+ }
-#if !AlwaysReload
-- (void) confirmButtonClicked {
- if (essential_ != nil)
- [essential_ show];
- else
- [self complete];
+ if (highlighted)
+ UISetColor(White_);
+
+ if (!highlighted)
+ UISetColor(commercial_ ? Purple_ : Black_);
+ [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
+ [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
+
+ if (!highlighted)
+ UISetColor(commercial_ ? Purplish_ : Gray_);
+ [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation];
+
+ if (placard_ != nil)
+ [placard_ drawAtPoint:CGPointMake(width - 52, 9)];
}
-#endif
@end
/* }}} */
+/* Section Cell {{{ */
+@interface SectionCell : CyteTableViewCell <
+ CyteTableViewCellDelegate
+> {
+ _H<NSString> basic_;
+ _H<NSString> section_;
+ _H<NSString> name_;
+ _H<NSString> count_;
+ _H<UIImage> icon_;
+ _H<UISwitch> switch_;
+ BOOL editing_;
+}
-/* Progress Data {{{ */
-@interface CydiaProgressData : NSObject {
- _transient id delegate_;
+- (void) setSection:(Section *)section editing:(BOOL)editing;
- bool running_;
- float percent_;
+@end
- float current_;
- float total_;
- float speed_;
+@implementation SectionCell
- _H<NSMutableArray> events_;
- _H<NSString> title_;
-
- _H<NSString> status_;
- _H<NSString> finish_;
-}
-
-@end
-
-@implementation CydiaProgressData
-
-+ (NSArray *) _attributeKeys {
- return [NSArray arrayWithObjects:
- @"current",
- @"events",
- @"finish",
- @"percent",
- @"running",
- @"speed",
- @"title",
- @"total",
- nil];
-}
+- (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
+ if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
+ icon_ = [UIImage applicationImageNamed:@"folder.png"];
+ switch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(218, 9, 60, 25)] autorelease];
+ [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventValueChanged];
-- (NSArray *) attributeKeys {
- return [[self class] _attributeKeys];
-}
+ UIView *content([self contentView]);
+ CGRect bounds([content bounds]);
-+ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
- return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
-}
+ content_ = [[[CyteTableViewCellContentView alloc] initWithFrame:bounds] autorelease];
+ [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [content addSubview:content_];
+ [content_ setBackgroundColor:[UIColor whiteColor]];
-- (id) init {
- if ((self = [super init]) != nil) {
- events_ = [NSMutableArray arrayWithCapacity:32];
+ [content_ setDelegate:self];
} return self;
}
-- (void) setDelegate:(id)delegate {
- delegate_ = delegate;
-}
+- (void) onSwitch:(id)sender {
+ NSMutableDictionary *metadata([Sections_ objectForKey:basic_]);
+ if (metadata == nil) {
+ metadata = [NSMutableDictionary dictionaryWithCapacity:2];
+ [Sections_ setObject:metadata forKey:basic_];
+ }
-- (void) setPercent:(float)value {
- percent_ = value;
+ [metadata setObject:[NSNumber numberWithBool:([switch_ isOn] == NO)] forKey:@"Hidden"];
+ Changed_ = true;
}
-- (NSNumber *) percent {
- return [NSNumber numberWithFloat:percent_];
-}
+- (void) setSection:(Section *)section editing:(BOOL)editing {
+ if (editing != editing_) {
+ if (editing_)
+ [switch_ removeFromSuperview];
+ else
+ [self addSubview:switch_];
+ editing_ = editing;
+ }
-- (void) setCurrent:(float)value {
- current_ = value;
-}
+ basic_ = nil;
+ section_ = nil;
+ name_ = nil;
+ count_ = nil;
-- (NSNumber *) current {
- return [NSNumber numberWithFloat:current_];
-}
+ if (section == nil) {
+ name_ = UCLocalize("ALL_PACKAGES");
+ count_ = nil;
+ } else {
+ basic_ = [section name];
+ section_ = [section localized];
-- (void) setTotal:(float)value {
- total_ = value;
-}
+ name_ = section_ == nil || [section_ length] == 0 ? UCLocalize("NO_SECTION") : (NSString *) section_;
+ count_ = [NSString stringWithFormat:@"%d", [section count]];
-- (NSNumber *) total {
- return [NSNumber numberWithFloat:total_];
-}
+ if (editing_)
+ [switch_ setOn:(isSectionVisible(basic_) ? 1 : 0) animated:NO];
+ }
-- (void) setSpeed:(float)value {
- speed_ = value;
-}
+ [self setAccessoryType:editing ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator];
+ [self setSelectionStyle:editing ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleBlue];
-- (NSNumber *) speed {
- return [NSNumber numberWithFloat:speed_];
+ [content_ setNeedsDisplay];
}
-- (NSArray *) events {
- return events_;
-}
+- (void) setFrame:(CGRect)frame {
+ [super setFrame:frame];
-- (void) removeAllEvents {
- [events_ removeAllObjects];
+ CGRect rect([switch_ frame]);
+ [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)];
}
-- (void) addEvent:(CydiaProgressEvent *)event {
- [events_ addObject:event];
+- (NSString *) accessibilityLabel {
+ return name_;
}
-- (void) setTitle:(NSString *)text {
- title_ = text;
-}
+- (void) drawContentRect:(CGRect)rect {
+ bool highlighted(highlighted_ && !editing_);
-- (NSString *) title {
- return title_;
-}
+ [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
-- (void) setFinish:(NSString *)text {
- finish_ = text;
-}
+ if (highlighted)
+ UISetColor(White_);
-- (NSString *) finish {
- return (id) finish_ ?: [NSNull null];
-}
+ float width(rect.size.width);
+ if (editing_)
+ width -= 87;
-- (void) setRunning:(bool)running {
- running_ = running;
-}
+ if (!highlighted)
+ UISetColor(Black_);
+ [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ lineBreakMode:UILineBreakModeTailTruncation];
-- (NSNumber *) running {
- return running_ ? (NSNumber *) kCFBooleanTrue : (NSNumber *) kCFBooleanFalse;
+ CGSize size = [count_ sizeWithFont:Font14_];
+
+ UISetColor(White_);
+ if (count_ != nil)
+ [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
}
@end
/* }}} */
-/* Progress Controller {{{ */
-@interface ProgressController : CydiaWebViewController <
- ProgressDelegate
+
+/* File Table {{{ */
+@interface FileTable : CyteViewController <
+ UITableViewDataSource,
+ UITableViewDelegate
> {
_transient Database *database_;
- _H<CydiaProgressData, 1> progress_;
- unsigned cancel_;
+ _H<Package> package_;
+ _H<NSString> name_;
+ _H<NSMutableArray> files_;
+ _H<UITableView, 2> list_;
}
-- (id) initWithDatabase:(Database *)database delegate:(id)delegate;
-
-- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title;
-
-- (void) setTitle:(NSString *)title;
-- (void) setCancellable:(bool)cancellable;
+- (id) initWithDatabase:(Database *)database;
+- (void) setPackage:(Package *)package;
@end
-@implementation ProgressController
+@implementation FileTable
-- (void) dealloc {
- [database_ setProgressDelegate:nil];
- [super dealloc];
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ return files_ == nil ? 0 : [files_ count];
}
-- (UIBarButtonItem *) leftButton {
- return cancel_ == 1 ? [[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("CANCEL")
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(cancel)
- ] autorelease] : nil;
+/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return 24.0f;
+}*/
+
+- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ static NSString *reuseIdentifier = @"Cell";
+
+ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+ if (cell == nil) {
+ cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
+ [cell setFont:[UIFont systemFontOfSize:16]];
+ }
+ [cell setText:[files_ objectAtIndex:indexPath.row]];
+ [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
+
+ return cell;
}
-- (void) updateCancel {
- [super applyLeftButton];
+- (NSURL *) navigationURL {
+ return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/files", [package_ id]]];
}
-- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
- if ((self = [super init]) != nil) {
- database_ = database;
- delegate_ = delegate;
+- (void) loadView {
+ list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
+ [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [list_ setRowHeight:24.0f];
+ [(UITableView *) list_ setDataSource:self];
+ [list_ setDelegate:self];
+ [self setView:list_];
+}
- [database_ setProgressDelegate:self];
+- (void) viewDidLoad {
+ [super viewDidLoad];
- progress_ = [[[CydiaProgressData alloc] init] autorelease];
- [progress_ setDelegate:self];
+ [[self navigationItem] setTitle:UCLocalize("INSTALLED_FILES")];
+}
- [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/progress/", UI_]]];
+- (void) releaseSubviews {
+ list_ = nil;
- [scroller_ setBackgroundColor:[UIColor blackColor]];
+ package_ = nil;
+ files_ = nil;
- [[self navigationItem] setHidesBackButton:YES];
+ [super releaseSubviews];
+}
- [self updateCancel];
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super init]) != nil) {
+ database_ = database;
} return self;
}
-- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
- [super webView:view didClearWindowObject:window forFrame:frame];
- [window setValue:progress_ forKey:@"cydiaProgress"];
-}
+- (void) setPackage:(Package *)package {
+ package_ = nil;
+ name_ = nil;
-- (void) updateProgress {
- [self dispatchEvent:@"CydiaProgressUpdate"];
-}
+ files_ = [NSMutableArray arrayWithCapacity:32];
-- (void) viewWillAppear:(BOOL)animated {
- [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack];
- [super viewWillAppear:animated];
-}
+ if (package != nil) {
+ package_ = package;
+ name_ = [package id];
-- (void) reloadSpringBoard {
- pid_t pid(ExecFork());
- if (pid == 0) {
- pid_t pid(ExecFork());
- if (pid == 0) {
- execl("/usr/bin/sbreload", "sbreload", NULL);
- perror("sbreload");
- exit(0);
- }
+ if (NSArray *files = [package files])
+ [files_ addObjectsFromArray:files];
- exit(0);
+ if ([files_ count] != 0) {
+ if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
+ [files_ removeObjectAtIndex:0];
+ [files_ sortUsingSelector:@selector(compareByPath:)];
+
+ NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
+ [stack addObject:@"/"];
+
+ for (int i(0), e([files_ count]); i != e; ++i) {
+ NSString *file = [files_ objectAtIndex:i];
+ while (![file hasPrefix:[stack lastObject]])
+ [stack removeLastObject];
+ NSString *directory = [stack lastObject];
+ [stack addObject:[file stringByAppendingString:@"/"]];
+ [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
+ ([stack count] - 2) * 3, "",
+ [file substringFromIndex:[directory length]]
+ ]];
+ }
+ }
}
- ReapZombie(pid);
+ [list_ reloadData];
+}
- sleep(15);
- system("/usr/bin/killall SpringBoard");
+- (void) reloadData {
+ [super reloadData];
+
+ [self setPackage:[database_ packageWithName:name_]];
}
-- (void) close {
- UpdateExternalStatus(0);
+@end
+/* }}} */
+/* Package Controller {{{ */
+@interface CYPackageController : CydiaWebViewController <
+ UIActionSheetDelegate
+> {
+ _transient Database *database_;
+ _H<Package> package_;
+ _H<NSString> name_;
+ bool commercial_;
+ _H<NSMutableArray> buttons_;
+ _H<UIBarButtonItem> button_;
+}
- if (Finish_ > 1)
- [delegate_ saveState];
+- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer;
- switch (Finish_) {
- case 0:
- [delegate_ returnToCydia];
- break;
+@end
- case 1:
- [delegate_ terminateWithSuccess];
- /*if ([delegate_ respondsToSelector:@selector(suspendWithAnimation:)])
- [delegate_ suspendWithAnimation:YES];
- else
- [delegate_ suspend];*/
- break;
+@implementation CYPackageController
- case 2:
- _trace();
- goto reload;
+- (NSURL *) navigationURL {
+ return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", (id) name_]];
+}
- case 3:
- _trace();
- goto reload;
+/* XXX: this is not safe at all... localization of /fail/ */
+- (void) _clickButtonWithName:(NSString *)name {
+ if ([name isEqualToString:UCLocalize("CLEAR")])
+ [delegate_ clearPackage:package_];
+ else if ([name isEqualToString:UCLocalize("INSTALL")])
+ [delegate_ installPackage:package_];
+ else if ([name isEqualToString:UCLocalize("REINSTALL")])
+ [delegate_ installPackage:package_];
+ else if ([name isEqualToString:UCLocalize("REMOVE")])
+ [delegate_ removePackage:package_];
+ else if ([name isEqualToString:UCLocalize("UPGRADE")])
+ [delegate_ installPackage:package_];
+ else _assert(false);
- reload: {
- UIProgressHUD *hud([delegate_ addProgressHUD]);
- [hud setText:UCLocalize("LOADING")];
- [self performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5];
- return;
- }
+ UITabBar *tabBar = [[self tabBarController] tabBar];
+ UITabBarItem *lastItem = [[tabBar items] lastObject];
+ CGPoint endPoint = [[[lastItem view] superview] convertPoint:[[lastItem view] frame].origin toView:nil];
+ endPoint = CGPointMake(endPoint.x + [[lastItem view] frame].size.width / 2, endPoint.y + [[lastItem view] frame].size.height / 2);
- case 4:
- _trace();
- if (void (*SBReboot)(mach_port_t) = reinterpret_cast<void (*)(mach_port_t)>(dlsym(RTLD_DEFAULT, "SBReboot")))
- SBReboot(SBSSpringBoardServerPort());
- else
- reboot2(RB_AUTOBOOT);
- break;
- }
+ UIBarButtonItem *rightItem = [[self navigationItem] rightBarButtonItem];
+ CGPoint startPoint = [[[rightItem view] superview] convertPoint:[[rightItem view] frame].origin toView:nil];
+ startPoint = CGPointMake(startPoint.x + [[rightItem view] frame].size.width / 2, startPoint.y + [[rightItem view] frame].size.height / 2);
- [super close];
-}
+ NSLog(@"animating from %@ to %@", NSStringFromCGPoint(startPoint), NSStringFromCGPoint(endPoint));
-- (void) setTitle:(NSString *)title {
- [progress_ setTitle:title];
- [self updateProgress];
-}
+ // Determine the animation's path.
+ CGPoint curvePoint1 = CGPointMake(startPoint.x - 130, startPoint.y - 10);
+ CGPoint curvePoint2 = CGPointMake(startPoint.x - 140, endPoint.y - 40);
-- (UIBarButtonItem *) rightButton {
- return [[progress_ running] boolValue] ? [super rightButton] : [[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("CLOSE")
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(close)
- ] autorelease];
+ // Create the animation's path.
+ CGPathRef path = NULL;
+ CGMutablePathRef mutablepath = CGPathCreateMutable();
+ CGPathMoveToPoint(mutablepath, NULL, startPoint.x, startPoint.y);
+
+ CGPathAddCurveToPoint(mutablepath, NULL,
+ curvePoint1.x, curvePoint1.y,
+ curvePoint2.x, curvePoint2.y,
+ endPoint.x, endPoint.y);
+
+ path = CGPathCreateCopy(mutablepath);
+ CGPathRelease(mutablepath);
+
+ UIImageView* animatedLabel = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"packages.png"]];
+ animatedLabel.tag = 12345;
+ [[[self view] window] addSubview:animatedLabel];
+ [animatedLabel release];
+ CALayer *iconViewLayer = animatedLabel.layer;
+
+ CAKeyframeAnimation *animatedIconAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
+ animatedIconAnimation.removedOnCompletion = YES;
+ animatedIconAnimation.duration = 0.5;
+ animatedIconAnimation.delegate = self;
+ animatedIconAnimation.path = path;
+ animatedIconAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
+ [iconViewLayer addAnimation:animatedIconAnimation forKey:@"animateIcon"];
+
+ // Start the icon animation.
+ [iconViewLayer setPosition:CGPointMake(endPoint.x, endPoint.y)];
+
+ [UIView beginAnimations:nil context:iconViewLayer];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(flyAnimationCompleted:finished:context:)];
+ [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
+ [UIView setAnimationDuration:0.5];
+ [animatedLabel setTransform:CGAffineTransformMakeScale(0.3, 0.3)];
+ [UIView commitAnimations];
}
-- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title {
- UpdateExternalStatus(1);
+- (void) flyAnimationCompleted:(NSString *)animation finished:(BOOL)finished context:(void *)context {
+ CALayer *layer = (CALayer *) context;
+ [layer removeFromSuperlayer];
+}
- [progress_ setRunning:true];
- [self setTitle:title];
- // implicit updateProgress
+- (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
+ NSString *context([sheet context]);
- SHA1SumValue notifyconf; {
- FileFd file;
- if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
- _error->Discard();
- else {
- MMap mmap(file, MMap::ReadOnly);
- SHA1Summation sha1;
- sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
- notifyconf = sha1.Result();
+ if ([context isEqualToString:@"modify"]) {
+ if (button != [sheet cancelButtonIndex]) {
+ NSString *buttonName = [buttons_ objectAtIndex:button];
+ [self _clickButtonWithName:buttonName];
}
- }
- SHA1SumValue springlist; {
- FileFd file;
- if (!file.Open(SpringBoard_, FileFd::ReadOnly))
- _error->Discard();
- else {
- MMap mmap(file, MMap::ReadOnly);
- SHA1Summation sha1;
- sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
- springlist = sha1.Result();
- }
+ [sheet dismissWithClickedButtonIndex:-1 animated:YES];
}
+}
- if (invocation != nil) {
- [invocation yieldToSelector:@selector(invoke)];
- [self setTitle:@"COMPLETE"];
- }
+- (bool) _allowJavaScriptPanel {
+ return commercial_;
+}
- if (Finish_ < 4) {
- FileFd file;
- if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
- _error->Discard();
- else {
- MMap mmap(file, MMap::ReadOnly);
- SHA1Summation sha1;
- sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
- if (!(notifyconf == sha1.Result()))
- Finish_ = 4;
- }
- }
+#if !AlwaysReload
+- (void) _customButtonClicked {
+ int count([buttons_ count]);
+ if (count == 0)
+ return;
- if (Finish_ < 3) {
- FileFd file;
- if (!file.Open(SpringBoard_, FileFd::ReadOnly))
- _error->Discard();
- else {
- MMap mmap(file, MMap::ReadOnly);
- SHA1Summation sha1;
- sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
- if (!(springlist == sha1.Result()))
- Finish_ = 3;
- }
- }
+ if (count == 1)
+ [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
+ else {
+ NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:count];
+ [buttons addObjectsFromArray:buttons_];
- if (Finish_ < 2) {
- if (RestartSubstrate_)
- Finish_ = 2;
- }
+ UIActionSheet *sheet = [[[UIActionSheet alloc]
+ initWithTitle:nil
+ delegate:self
+ cancelButtonTitle:nil
+ destructiveButtonTitle:nil
+ otherButtonTitles:nil
+ ] autorelease];
- RestartSubstrate_ = false;
+ for (NSString *button in buttons) [sheet addButtonWithTitle:button];
+ if (!IsWildcat_) {
+ [sheet addButtonWithTitle:UCLocalize("CANCEL")];
+ [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
+ }
+ [sheet setContext:@"modify"];
- switch (Finish_) {
- case 0: [progress_ setFinish:UCLocalize("RETURN_TO_CYDIA")]; break; /* XXX: Maybe UCLocalize("DONE")? */
- case 1: [progress_ setFinish:UCLocalize("CLOSE_CYDIA")]; break;
- case 2: [progress_ setFinish:UCLocalize("RESTART_SPRINGBOARD")]; break;
- case 3: [progress_ setFinish:UCLocalize("RELOAD_SPRINGBOARD")]; break;
- case 4: [progress_ setFinish:UCLocalize("REBOOT_DEVICE")]; break;
+ [delegate_ showActionSheet:sheet fromItem:[[self navigationItem] rightBarButtonItem]];
}
+}
- UpdateExternalStatus(Finish_ == 0 ? 0 : 2);
-
- [progress_ setRunning:false];
- [self updateProgress];
+// We don't want to allow non-commercial packages to do custom things to the install button,
+// so it must call customButtonClicked with a custom commercial_ == 1 fallthrough.
+- (void) customButtonClicked {
+ if (commercial_)
+ [super customButtonClicked];
+ else
+ [self _customButtonClicked];
+}
- [self applyRightButton];
+- (void) reloadButtonClicked {
+ // Don't reload a commerical package by tapping the loading button,
+ // but if it's not an Install button, we should forward it on.
+ if (![package_ uninstalled])
+ [self _customButtonClicked];
}
-- (void) addProgressEvent:(CydiaProgressEvent *)event {
- [progress_ addEvent:event];
- [self updateProgress];
+- (void) applyLoadingTitle {
+ // Don't show "Loading" as the title. Ever.
}
-- (bool) isProgressCancelled {
- return cancel_ == 2;
+- (UIBarButtonItem *) rightButton {
+ return button_;
}
+#endif
-- (void) cancel {
- cancel_ = 2;
- [self updateCancel];
+- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer {
+ if ((self = [super init]) != nil) {
+ database_ = database;
+ buttons_ = [NSMutableArray arrayWithCapacity:4];
+ name_ = name == nil ? @"" : [NSString stringWithString:name];
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/package/%@", UI_, (id) name_]] withReferrer:referrer];
+ } return self;
}
-- (void) setCancellable:(bool)cancellable {
- unsigned cancel(cancel_);
+- (void) reloadData {
+ [super reloadData];
- if (!cancellable)
- cancel_ = 0;
- else if (cancel_ == 0)
- cancel_ = 1;
+ package_ = [database_ packageWithName:name_];
- if (cancel != cancel_)
- [self updateCancel];
-}
+ [buttons_ removeAllObjects];
-- (void) setProgressCancellable:(NSNumber *)cancellable {
- [self setCancellable:[cancellable boolValue]];
-}
+ if (package_ != nil) {
+ [(Package *) package_ parse];
-- (void) setProgressPercent:(NSNumber *)percent {
- [progress_ setPercent:[percent floatValue]];
- [self updateProgress];
-}
+ commercial_ = [package_ isCommercial];
-- (void) setProgressStatus:(NSDictionary *)status {
- if (status == nil) {
- [progress_ setCurrent:0];
- [progress_ setTotal:0];
- [progress_ setSpeed:0];
- } else {
- [progress_ setPercent:[[status objectForKey:@"Percent"] floatValue]];
+ if ([package_ mode] != nil)
+ [buttons_ addObject:UCLocalize("CLEAR")];
+ if ([package_ source] == nil);
+ else if ([package_ upgradableAndEssential:NO])
+ [buttons_ addObject:UCLocalize("UPGRADE")];
+ else if ([package_ uninstalled])
+ [buttons_ addObject:UCLocalize("INSTALL")];
+ else
+ [buttons_ addObject:UCLocalize("REINSTALL")];
+ if (![package_ uninstalled])
+ [buttons_ addObject:UCLocalize("REMOVE")];
+ }
- [progress_ setCurrent:[[status objectForKey:@"Current"] floatValue]];
- [progress_ setTotal:[[status objectForKey:@"Total"] floatValue]];
- [progress_ setSpeed:[[status objectForKey:@"Speed"] floatValue]];
+ NSString *title;
+ switch ([buttons_ count]) {
+ case 0: title = nil; break;
+ case 1: title = [buttons_ objectAtIndex:0]; break;
+ default: title = UCLocalize("MODIFY"); break;
}
- [self updateProgress];
+ button_ = [[[UIBarButtonItem alloc]
+ initWithTitle:title
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(customButtonClicked)
+ ] autorelease];
+}
+
+- (bool) isLoading {
+ return commercial_ ? [super isLoading] : false;
}
@end
/* }}} */
-/* Package Cell {{{ */
-@interface PackageCell : CyteTableViewCell <
- CyteTableViewCellDelegate
-> {
- _H<UIImage> icon_;
- _H<NSString> name_;
- _H<NSString> description_;
- bool commercial_;
- _H<NSString> source_;
- _H<UIImage> badge_;
- _H<UIImage> placard_;
-}
+/* Package List Controller {{{ */
-- (PackageCell *) init;
-- (void) setPackage:(Package *)package;
+// This is used to sort the filters. We want this to ensure that the
+// cheapest filters run first. This is probably a premature optimization.
+typedef enum {
+ kPackageListFilterPriorityLow,
+ kPackageListFilterPriorityNormal,
+ kPackageListFilterPriorityHigh
+} PackageListFilterPriority;
-- (void) drawContentRect:(CGRect)rect;
+typedef struct {
+ SEL selector;
+ id object;
+ PackageListFilterPriority priority;
+ IMP implementation;
+} PackageListFilter;
-@end
+@interface NSValue(PackageListFilter)
-@implementation PackageCell
++ (id)valueWithPackageListFilter:(PackageListFilter)filter;
+- (PackageListFilter)packageListFilterValue;
-- (PackageCell *) init {
- CGRect frame(CGRectMake(0, 0, 320, 74));
- if ((self = [super initWithFrame:frame reuseIdentifier:@"Package"]) != nil) {
- UIView *content([self contentView]);
- CGRect bounds([content bounds]);
+@end
- content_ = [[[CyteTableViewCellContentView alloc] initWithFrame:bounds] autorelease];
- [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [content addSubview:content_];
+@implementation NSValue(PackageListFilter)
- [content_ setDelegate:self];
- [content_ setOpaque:YES];
- } return self;
++ (id)valueWithPackageListFilter:(PackageListFilter)filter {
+ return [NSValue valueWithBytes:&filter objCType:@encode(PackageListFilter)];
}
-- (NSString *) accessibilityLabel {
- return [NSString stringWithFormat:UCLocalize("COLON_DELIMITED"), (id) name_, (id) description_];
+- (PackageListFilter)packageListFilterValue {
+ PackageListFilter filter;
+ [self getValue:&filter];
+ return filter;
}
-- (void) setPackage:(Package *)package {
- icon_ = nil;
- name_ = nil;
- description_ = nil;
- source_ = nil;
- badge_ = nil;
- placard_ = nil;
+@end
- if (package == nil)
- [content_ setBackgroundColor:[UIColor whiteColor]];
- else {
- [package parse];
+@interface FilteredPackageListDataSource : NSObject <UITableViewDataSource> {
+ _transient Database *database_;
+ unsigned era_;
+ _H<NSArray> packages_;
+ _H<NSMutableArray> sections_;
+ _H<NSMutableArray> index_;
+ _H<NSMutableDictionary> indices_;
+ _H<NSMutableDictionary> filters_;
+}
- Source *source = [package source];
+- (id)objectForFilter:(NSString *)filter;
+- (void)setObject:(id)object forFilter:(NSString *)filter;
+- (SEL)selectorForFilter:(NSString *)filter;
+- (void)setSelector:(SEL)selector forFilter:(NSString *)filter;
+- (PackageListFilterPriority)priorityForFilter:(NSString *)filter;
+- (void)setPriority:(PackageListFilterPriority)priority forFilter:(NSString *)filter;
- icon_ = [package icon];
+- (void)addFilter:(NSString *)filter withSelector:(SEL)selector;
+- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority;
+- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority object:(id)object;
+- (void)removeFilter:(NSString *)filter;
- if (NSString *name = [package name])
- name_ = [NSString stringWithString:name];
+- (NSArray *)packages;
- NSString *description(nil);
+@end
- if (description == nil && IsWildcat_)
- description = [package longDescription];
- if (description == nil)
- description = [package shortDescription];
+@implementation FilteredPackageListDataSource
- if (description != nil)
- description_ = [NSString stringWithString:description];
+- (NSArray *) packages {
+ return packages_;
+}
- commercial_ = [package isCommercial];
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super init]) != nil) {
+ database_ = database;
+ filters_ = [NSMutableDictionary dictionary];
+ } return self;
+}
- NSString *label = nil;
- bool trusted = false;
++ (BOOL) supportsSearch {
+ return YES;
+}
- if (source != nil) {
- label = [source label];
- trusted = [source trusted];
- } else if ([[package id] isEqualToString:@"firmware"])
- label = UCLocalize("APPLE");
- else
- label = [NSString stringWithFormat:UCLocalize("SLASH_DELIMITED"), UCLocalize("UNKNOWN"), UCLocalize("LOCAL")];
+- (Package *) packageAtIndexPath:(NSIndexPath *)path {
+@synchronized (database_) {
+ if ([database_ era] != era_)
+ return nil;
- NSString *from(label);
+ Section *section([sections_ objectAtIndex:[path section]]);
+ NSInteger row([path row]);
+ Package *package([packages_ objectAtIndex:([section row] + row)]);
+ return [[package retain] autorelease];
+} }
+
+- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView {
+ if ([[self class] supportsSearch])
+ return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:index_];
+ else
+ return index_;
+}
- NSString *section = [package simpleSection];
- if (section != nil && ![section isEqualToString:label]) {
- section = [[NSBundle mainBundle] localizedStringForKey:section value:nil table:@"Sections"];
- from = [NSString stringWithFormat:UCLocalize("PARENTHETICAL"), from, section];
- }
+- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
+ PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
+ if (cell == nil)
+ cell = [[[PackageCell alloc] init] autorelease];
- source_ = [NSString stringWithFormat:UCLocalize("FROM"), from];
+ Package *package([database_ packageWithName:[[self packageAtIndexPath:path] id]]);
+ [cell setPackage:package];
+ return cell;
+}
- if (NSString *purpose = [package primaryPurpose])
- badge_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/Purposes/%@.png", App_, purpose]];
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
+ NSInteger count([sections_ count]);
+ return count == 0 ? 1 : count;
+}
- UIColor *color;
- NSString *placard;
+- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
+ if ([sections_ count] == 0 || [[sections_ objectAtIndex:section] count] == 0)
+ return nil;
+ return [[sections_ objectAtIndex:section] name];
+}
- if (NSString *mode = [package mode]) {
- if ([mode isEqualToString:@"REMOVE"] || [mode isEqualToString:@"PURGE"]) {
- color = RemovingColor_;
- //placard = @"removing";
- } else {
- color = InstallingColor_;
- //placard = @"installing";
- }
+- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
+ if ([sections_ count] == 0)
+ return 0;
+ return [[sections_ objectAtIndex:section] count];
+}
- // XXX: the removing/installing placards are not @2x
- placard = nil;
+- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
+ if ([[self class] supportsSearch]) {
+ if (index == 0) {
+ [tableView setContentOffset:CGPointZero animated:NO];
+ return NSNotFound;
} else {
- color = [UIColor whiteColor];
-
- if ([package installed] != nil)
- placard = @"installed";
- else
- placard = nil;
+ return index - 1;
}
-
- [content_ setBackgroundColor:color];
-
- if (placard != nil)
- placard_ = [UIImage imageAtPath:[NSString stringWithFormat:@"%@/%@.png", App_, placard]];
}
- [self setNeedsDisplay];
- [content_ setNeedsDisplay];
+ return index;
}
-- (void) drawContentRect:(CGRect)rect {
- bool highlighted(highlighted_);
- float width([self bounds].size.width);
+- (IMP)implementationForSelector:(SEL)selector {
+@synchronized (self) {
+ /* XXX: this is an unsafe optimization of doomy hell */
+ Method method(class_getInstanceMethod([Package class], selector));
+ _assert(method != NULL);
+ IMP imp = method_getImplementation(method);
+ _assert(imp != NULL);
- if (icon_ != nil) {
- CGRect rect;
- rect.size = [(UIImage *) icon_ size];
+ return imp;
+} }
- while (rect.size.width > 32 || rect.size.height > 32) {
- rect.size.width /= 2;
- rect.size.height /= 2;
- }
+- (PackageListFilter)packageListFilterForFilter:(NSString *)filter {
+ return [[filters_ objectForKey:filter] packageListFilterValue];
+}
- rect.origin.x = 25 - rect.size.width / 2;
- rect.origin.y = 25 - rect.size.height / 2;
+- (void)setPackageListFilter:(PackageListFilter)filter forFilter:(NSString *)filterName {
+ [filters_ setObject:[NSValue valueWithPackageListFilter:filter] forKey:filterName];
+}
- [icon_ drawInRect:rect];
- }
+- (id)objectForFilter:(NSString *)filter {
+ return [self packageListFilterForFilter:filter].object;
+}
- if (badge_ != nil) {
- CGRect rect;
- rect.size = [(UIImage *) badge_ size];
+- (SEL)selectorForFilter:(NSString *)filter {
+ return [self packageListFilterForFilter:filter].selector;
+}
- rect.size.width /= 2;
- rect.size.height /= 2;
+- (IMP)implementationForFilter:(NSString *)filter {
+ return [self packageListFilterForFilter:filter].implementation;
+}
- rect.origin.x = 36 - rect.size.width / 2;
- rect.origin.y = 36 - rect.size.height / 2;
+- (PackageListFilterPriority)priorityForFilter:(NSString *)filter {
+ return [self packageListFilterForFilter:filter].priority;
+}
- [badge_ drawInRect:rect];
- }
+- (void)setObject:(id)object forFilter:(NSString *)filterName {
+ PackageListFilter filter = [self packageListFilterForFilter:filterName];
+ filter.object = object;
+ [self setPackageListFilter:filter forFilter:filterName];
+}
- if (highlighted)
- UISetColor(White_);
+- (void)setPriority:(PackageListFilterPriority)priority forFilter:(NSString *)filterName {
+ PackageListFilter filter = [self packageListFilterForFilter:filterName];
+ filter.priority = priority;
+ [self setPackageListFilter:filter forFilter:filterName];
+}
- if (!highlighted)
- UISetColor(commercial_ ? Purple_ : Black_);
- [name_ drawAtPoint:CGPointMake(48, 8) forWidth:(width - (placard_ == nil ? 80 : 106)) withFont:Font18Bold_ lineBreakMode:UILineBreakModeTailTruncation];
- [source_ drawAtPoint:CGPointMake(58, 29) forWidth:(width - 95) withFont:Font12_ lineBreakMode:UILineBreakModeTailTruncation];
+- (void)setImplementation:(IMP)implementation forFilter:(NSString *)filterName {
+ PackageListFilter filter = [self packageListFilterForFilter:filterName];
+ filter.implementation = implementation;
+ [self setPackageListFilter:filter forFilter:filterName];
+}
- if (!highlighted)
- UISetColor(commercial_ ? Purplish_ : Gray_);
- [description_ drawAtPoint:CGPointMake(12, 46) forWidth:(width - 46) withFont:Font14_ lineBreakMode:UILineBreakModeTailTruncation];
+- (void)setSelector:(SEL)selector forFilter:(NSString *)filterName {
+ PackageListFilter filter = [self packageListFilterForFilter:filterName];
+ filter.selector = selector;
+ [self setPackageListFilter:filter forFilter:filterName];
- if (placard_ != nil)
- [placard_ drawAtPoint:CGPointMake(width - 52, 9)];
+ [self setImplementation:[self implementationForSelector:selector] forFilter:filterName];
}
-@end
-/* }}} */
-/* Section Cell {{{ */
-@interface SectionCell : CyteTableViewCell <
- CyteTableViewCellDelegate
-> {
- _H<NSString> basic_;
- _H<NSString> section_;
- _H<NSString> name_;
- _H<NSString> count_;
- _H<UIImage> icon_;
- _H<UISwitch> switch_;
- BOOL editing_;
-}
+- (void)addFilter:(NSString *)filterName {
+ PackageListFilter filter;
+ filter.object = nil;
+ filter.selector = NULL;
+ filter.implementation = NULL;
+ filter.priority = kPackageListFilterPriorityNormal;
-- (void) setSection:(Section *)section editing:(BOOL)editing;
+ [self setPackageListFilter:filter forFilter:filterName];
+}
-@end
+- (void)addFilter:(NSString *)filter withSelector:(SEL)selector {
+ [self addFilter:filter];
+ [self setSelector:selector forFilter:filter];
+}
-@implementation SectionCell
+- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority {
+ [self addFilter:filter withSelector:selector];
+ [self setPriority:priority forFilter:filter];
+}
-- (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
- if ((self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) != nil) {
- icon_ = [UIImage applicationImageNamed:@"folder.png"];
- switch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(218, 9, 60, 25)] autorelease];
- [switch_ addTarget:self action:@selector(onSwitch:) forEvents:UIControlEventValueChanged];
+- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority object:(id)object {
+ [self addFilter:filter withSelector:selector];
+ [self setObject:object forFilter:filter];
+}
- UIView *content([self contentView]);
- CGRect bounds([content bounds]);
+- (void)removeFilter:(NSString *)filter {
+ [filters_ removeObjectForKey:filter];
+}
- content_ = [[[CyteTableViewCellContentView alloc] initWithFrame:bounds] autorelease];
- [content_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [content addSubview:content_];
- [content_ setBackgroundColor:[UIColor whiteColor]];
+- (NSMutableArray *) _reloadPackages {
+@synchronized (database_) {
+ era_ = [database_ era];
- [content_ setDelegate:self];
- } return self;
-}
+ NSArray *packages([database_ packages]);
+ NSMutableArray *filtered;
-- (void) onSwitch:(id)sender {
- NSMutableDictionary *metadata([Sections_ objectForKey:basic_]);
- if (metadata == nil) {
- metadata = [NSMutableDictionary dictionaryWithCapacity:2];
- [Sections_ setObject:metadata forKey:basic_];
- }
+ NSArray *filters = [filters_ allKeys];
- [metadata setObject:[NSNumber numberWithBool:([switch_ isOn] == NO)] forKey:@"Hidden"];
- Changed_ = true;
-}
+ for (PackageListFilterPriority currentPriority = kPackageListFilterPriorityHigh; currentPriority >= kPackageListFilterPriorityLow; currentPriority = static_cast<PackageListFilterPriority>(static_cast<int>(currentPriority) - 1)) {
+ for (NSString *filterName in filters) {
+ PackageListFilter filter = [self packageListFilterForFilter:filterName];
-- (void) setSection:(Section *)section editing:(BOOL)editing {
- if (editing != editing_) {
- if (editing_)
- [switch_ removeFromSuperview];
- else
- [self addSubview:switch_];
- editing_ = editing;
- }
+ if (filter.priority == currentPriority) {
+ filtered = [NSMutableArray arrayWithCapacity:[packages count]];
- basic_ = nil;
- section_ = nil;
- name_ = nil;
- count_ = nil;
+ IMP implementation;
+ SEL selector;
+ _H<NSObject> object;
- if (section == nil) {
- name_ = UCLocalize("ALL_PACKAGES");
- count_ = nil;
- } else {
- basic_ = [section name];
- section_ = [section localized];
+ @synchronized (self) {
+ implementation = filter.implementation;
+ selector = filter.selector;
+ object = filter.object;
+ }
- name_ = section_ == nil || [section_ length] == 0 ? UCLocalize("NO_SECTION") : (NSString *) section_;
- count_ = [NSString stringWithFormat:@"%d", [section count]];
+ if (implementation == NULL) continue;
- if (editing_)
- [switch_ setOn:(isSectionVisible(basic_) ? 1 : 0) animated:NO];
+ _profile(PackageTable$reloadData$Filter)
+ for (Package *package in packages)
+ if ([package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(implementation))(package, selector, object))
+ [filtered addObject:package];
+ _end
+
+ packages = filtered;
+ }
+ }
}
- [self setAccessoryType:editing ? UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator];
- [self setSelectionStyle:editing ? UITableViewCellSelectionStyleNone : UITableViewCellSelectionStyleBlue];
+ // packages would also be valid here, but it's defined
+ // as an immutable array. filtered works too, so use it.
+ return filtered;
+} }
- [content_ setNeedsDisplay];
-}
+- (void) reloadData {
+ NSArray *packages;
-- (void) setFrame:(CGRect)frame {
- [super setFrame:frame];
+ reload:
+ packages = [self _reloadPackages];
- CGRect rect([switch_ frame]);
- [switch_ setFrame:CGRectMake(frame.size.width - 102, 9, rect.size.width, rect.size.height)];
-}
+@synchronized (database_) {
+ if (era_ != [database_ era])
+ goto reload;
-- (NSString *) accessibilityLabel {
- return name_;
-}
+ packages_ = packages;
-- (void) drawContentRect:(CGRect)rect {
- bool highlighted(highlighted_ && !editing_);
+ indices_ = [NSMutableDictionary dictionaryWithCapacity:32];
+ sections_ = [NSMutableArray arrayWithCapacity:16];
- [icon_ drawInRect:CGRectMake(8, 7, 32, 32)];
+ Section *section = nil;
+ index_ = [NSMutableArray arrayWithCapacity:32];
- if (highlighted)
- UISetColor(White_);
+ _profile(PackageTable$reloadData$Section)
+ for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
+ Package *package;
+ unichar index;
- float width(rect.size.width);
- if (editing_)
- width -= 87;
+ _profile(PackageTable$reloadData$Section$Package)
+ package = [packages_ objectAtIndex:offset];
+ index = [package index];
+ _end
- if (!highlighted)
- UISetColor(Black_);
- [name_ drawAtPoint:CGPointMake(48, 9) forWidth:(width - 70) withFont:Font22Bold_ lineBreakMode:UILineBreakModeTailTruncation];
+ if (section == nil || [section index] != index) {
+ _profile(PackageTable$reloadData$Section$Allocate)
+ section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
+ _end
- CGSize size = [count_ sizeWithFont:Font14_];
+ [index_ addObject:[section name]];
+ //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index];
- UISetColor(White_);
- if (count_ != nil)
- [count_ drawAtPoint:CGPointMake(13 + (29 - size.width) / 2, 16) withFont:Font12Bold_];
-}
+ _profile(PackageTable$reloadData$Section$Add)
+ [sections_ addObject:section];
+ _end
+ }
+
+ [section addToCount];
+ }
+ _end
+} }
@end
-/* }}} */
-/* File Table {{{ */
-@interface FileTable : CyteViewController <
- UITableViewDataSource,
- UITableViewDelegate
-> {
+@interface FilteredPackageListController : CyteViewController <UITableViewDelegate, UISearchDisplayDelegate> {
_transient Database *database_;
- _H<Package> package_;
- _H<NSString> name_;
- _H<NSMutableArray> files_;
+ _H<UISearchDisplayController> searchController_;
+ _H<UISearchBar> searchBar_;
+ _H<FilteredPackageListDataSource> datasource_;
_H<UITableView, 2> list_;
+ _H<NSString> title_;
}
-- (id) initWithDatabase:(Database *)database;
-- (void) setPackage:(Package *)package;
+- (id) initWithDatabase:(Database *)database title:(NSString *)title;
+- (void) clearData;
@end
-@implementation FileTable
+@implementation FilteredPackageListController
-- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- return files_ == nil ? 0 : [files_ count];
+- (NSURL *) referrerURL {
+ return [self navigationURL];
}
-/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- return 24.0f;
-}*/
-
-- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- static NSString *reuseIdentifier = @"Cell";
-
- UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
- if (cell == nil) {
- cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
- [cell setFont:[UIFont systemFontOfSize:16]];
- }
- [cell setText:[files_ objectAtIndex:indexPath.row]];
- [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
-
- return cell;
+- (void) deselectWithAnimation:(BOOL)animated {
+ [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
}
-- (NSURL *) navigationURL {
- return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/files", [package_ id]]];
+- (void) viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ [self deselectWithAnimation:animated];
}
-- (void) loadView {
- list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
- [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [list_ setRowHeight:24.0f];
- [(UITableView *) list_ setDataSource:self];
- [list_ setDelegate:self];
- [self setView:list_];
+- (void) didSelectPackage:(Package *)package {
+ CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[[self referrerURL] absoluteString]] autorelease]);
+ [view setDelegate:delegate_];
+ [[self navigationController] pushViewController:view animated:YES];
}
-- (void) viewDidLoad {
- [super viewDidLoad];
+- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
+ FilteredPackageListDataSource *source = datasource_;
- [[self navigationItem] setTitle:UCLocalize("INSTALLED_FILES")];
+ Package *package([source packageAtIndexPath:path]);
+ package = [database_ packageWithName:[package id]];
+ [self didSelectPackage:package];
}
-- (void) releaseSubviews {
- list_ = nil;
-
- package_ = nil;
- files_ = nil;
-
- [super releaseSubviews];
++ (Class) dataSourceClass {
+ return [FilteredPackageListDataSource class];
}
-- (id) initWithDatabase:(Database *)database {
+- (id) initWithDatabase:(Database *)database title:(NSString *)title {
if ((self = [super init]) != nil) {
database_ = database;
- } return self;
-}
-
-- (void) setPackage:(Package *)package {
- package_ = nil;
- name_ = nil;
-
- files_ = [NSMutableArray arrayWithCapacity:32];
-
- if (package != nil) {
- package_ = package;
- name_ = [package id];
- if (NSArray *files = [package files])
- [files_ addObjectsFromArray:files];
-
- if ([files_ count] != 0) {
- if ([[files_ objectAtIndex:0] isEqualToString:@"/."])
- [files_ removeObjectAtIndex:0];
- [files_ sortUsingSelector:@selector(compareByPath:)];
-
- NSMutableArray *stack = [NSMutableArray arrayWithCapacity:8];
- [stack addObject:@"/"];
-
- for (int i(0), e([files_ count]); i != e; ++i) {
- NSString *file = [files_ objectAtIndex:i];
- while (![file hasPrefix:[stack lastObject]])
- [stack removeLastObject];
- NSString *directory = [stack lastObject];
- [stack addObject:[file stringByAppendingString:@"/"]];
- [files_ replaceObjectAtIndex:i withObject:[NSString stringWithFormat:@"%*s%@",
- ([stack count] - 2) * 3, "",
- [file substringFromIndex:[directory length]]
- ]];
- }
- }
- }
-
- [list_ reloadData];
-}
-
-- (void) reloadData {
- [super reloadData];
-
- [self setPackage:[database_ packageWithName:name_]];
-}
+ datasource_ = [[[[[self class] dataSourceClass] alloc] initWithDatabase:database_] autorelease];
-@end
-/* }}} */
-/* Package Controller {{{ */
-@interface CYPackageController : CydiaWebViewController <
- UIActionSheetDelegate
-> {
- _transient Database *database_;
- _H<Package> package_;
- _H<NSString> name_;
- bool commercial_;
- _H<NSMutableArray> buttons_;
- _H<UIBarButtonItem> button_;
+ title_ = [title copy];
+ [[self navigationItem] setTitle:title_];
+ } return self;
}
-- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer;
-
-@end
-
-@implementation CYPackageController
-
-- (NSURL *) navigationURL {
- return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@", (id) name_]];
-}
+- (void) loadView {
+ UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]);
+ [view setBackgroundColor:[UIColor whiteColor]];
+ [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
+ [self setView:view];
-/* XXX: this is not safe at all... localization of /fail/ */
-- (void) _clickButtonWithName:(NSString *)name {
- if ([name isEqualToString:UCLocalize("CLEAR")])
- [delegate_ clearPackage:package_];
- else if ([name isEqualToString:UCLocalize("INSTALL")])
- [delegate_ installPackage:package_];
- else if ([name isEqualToString:UCLocalize("REINSTALL")])
- [delegate_ installPackage:package_];
- else if ([name isEqualToString:UCLocalize("REMOVE")])
- [delegate_ removePackage:package_];
- else if ([name isEqualToString:UCLocalize("UPGRADE")])
- [delegate_ installPackage:package_];
- else _assert(false);
-}
+ list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain] autorelease];
+ [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [view addSubview:list_];
-- (void) actionSheet:(UIActionSheet *)sheet clickedButtonAtIndex:(NSInteger)button {
- NSString *context([sheet context]);
+ // XXX: is 20 the most optimal number here?
+ [list_ setSectionIndexMinimumDisplayRowCount:20];
+ [list_ setRowHeight:73];
+ [(UITableView *) list_ setDataSource:datasource_];
+ [list_ setDelegate:self];
- if ([context isEqualToString:@"modify"]) {
- if (button != [sheet cancelButtonIndex]) {
- NSString *buttonName = [buttons_ objectAtIndex:button];
- [self _clickButtonWithName:buttonName];
- }
+ if ([[[self class] dataSourceClass] supportsSearch]) {
+ searchBar_ = [[UISearchBar alloc] init];
+ [searchBar_ sizeToFit];
+ searchController_ = [[UISearchDisplayController alloc] initWithSearchBar:searchBar_ contentsController:self];
+ [searchController_ setDelegate:self];
+ [list_ setTableHeaderView:searchBar_];
- [sheet dismissWithClickedButtonIndex:-1 animated:YES];
+ [searchController_ setSearchResultsDataSource:datasource_];
+ [searchController_ setSearchResultsDelegate:self];
}
}
-- (bool) _allowJavaScriptPanel {
- return commercial_;
+- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
+ [datasource_ addFilter:@"search" withSelector:@selector(isUnfilteredAndSelectedForBy:) priority:kPackageListFilterPriorityLow object:searchString];
+ [datasource_ reloadData];
+ return YES;
}
-#if !AlwaysReload
-- (void) _customButtonClicked {
- int count([buttons_ count]);
- if (count == 0)
- return;
+- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
+ [datasource_ removeFilter:@"search"];
+ [self reloadData];
+}
- if (count == 1)
- [self _clickButtonWithName:[buttons_ objectAtIndex:0]];
- else {
- NSMutableArray *buttons = [NSMutableArray arrayWithCapacity:count];
- [buttons addObjectsFromArray:buttons_];
+- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
+ // XXX: is 20 the most optimal number here?
+ [tableView setSectionIndexMinimumDisplayRowCount:20];
+ [tableView setRowHeight:73];
+}
- UIActionSheet *sheet = [[[UIActionSheet alloc]
- initWithTitle:nil
- delegate:self
- cancelButtonTitle:nil
- destructiveButtonTitle:nil
- otherButtonTitles:nil
- ] autorelease];
+- (void) releaseSubviews {
+ list_ = nil;
- for (NSString *button in buttons) [sheet addButtonWithTitle:button];
- if (!IsWildcat_) {
- [sheet addButtonWithTitle:UCLocalize("CANCEL")];
- [sheet setCancelButtonIndex:[sheet numberOfButtons] - 1];
- }
- [sheet setContext:@"modify"];
+ [super releaseSubviews];
+}
- [delegate_ showActionSheet:sheet fromItem:[[self navigationItem] rightBarButtonItem]];
- }
+- (void) reloadData {
+ [super reloadData];
+
+ [datasource_ reloadData];
+ [list_ reloadData];
}
-// We don't want to allow non-commercial packages to do custom things to the install button,
-// so it must call customButtonClicked with a custom commercial_ == 1 fallthrough.
-- (void) customButtonClicked {
- if (commercial_)
- [super customButtonClicked];
- else
- [self _customButtonClicked];
+- (void) resetScrollPosition {
+ [list_ scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
}
-- (void) reloadButtonClicked {
- // Don't reload a commerical package by tapping the loading button,
- // but if it's not an Install button, we should forward it on.
- if (![package_ uninstalled])
- [self _customButtonClicked];
+- (void) clearData {
+ [list_ setDataSource:nil];
+ [list_ reloadData];
+
+ [self resetScrollPosition];
}
-- (void) applyLoadingTitle {
- // Don't show "Loading" as the title. Ever.
+@end
+/* }}} */
+
+/* Home Controller {{{ */
+@interface HomeController : CydiaWebViewController {
+ CFRunLoopRef runloop_;
+ SCNetworkReachabilityRef reachability_;
}
-- (UIBarButtonItem *) rightButton {
- return button_;
+@end
+
+@implementation HomeController
+
+static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachability, SCNetworkReachabilityFlags flags, void *info) {
+ [(HomeController *) info dispatchEvent:@"CydiaReachabilityCallback"];
}
-#endif
-- (id) initWithDatabase:(Database *)database forPackage:(NSString *)name withReferrer:(NSString *)referrer {
+- (id) init {
if ((self = [super init]) != nil) {
- database_ = database;
- buttons_ = [NSMutableArray arrayWithCapacity:4];
- name_ = name == nil ? @"" : [NSString stringWithString:name];
- [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/package/%@", UI_, (id) name_]] withReferrer:referrer];
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/home/", UI_]]];
+ [self reloadData];
+
+ reachability_ = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "cydia.saurik.com");
+ if (reachability_ != NULL) {
+ SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
+ SCNetworkReachabilitySetCallback(reachability_, HomeControllerReachabilityCallback, &context);
+
+ CFRunLoopRef runloop(CFRunLoopGetCurrent());
+ if (SCNetworkReachabilityScheduleWithRunLoop(reachability_, runloop, kCFRunLoopDefaultMode))
+ runloop_ = runloop;
+ }
} return self;
}
-- (void) reloadData {
- [super reloadData];
-
- package_ = [database_ packageWithName:name_];
+- (void) dealloc {
+ if (reachability_ != NULL && runloop_ != NULL)
+ SCNetworkReachabilityUnscheduleFromRunLoop(reachability_, runloop_, kCFRunLoopDefaultMode);
+ [super dealloc];
+}
- [buttons_ removeAllObjects];
+- (NSURL *) navigationURL {
+ return [NSURL URLWithString:@"cydia://home"];
+}
- if (package_ != nil) {
- [(Package *) package_ parse];
+- (void) aboutButtonClicked {
+ UIAlertView *alert([[[UIAlertView alloc] init] autorelease]);
- commercial_ = [package_ isCommercial];
+ [alert setTitle:UCLocalize("ABOUT_CYDIA")];
+ [alert addButtonWithTitle:UCLocalize("CLOSE")];
+ [alert setCancelButtonIndex:0];
- if ([package_ mode] != nil)
- [buttons_ addObject:UCLocalize("CLEAR")];
- if ([package_ source] == nil);
- else if ([package_ upgradableAndEssential:NO])
- [buttons_ addObject:UCLocalize("UPGRADE")];
- else if ([package_ uninstalled])
- [buttons_ addObject:UCLocalize("INSTALL")];
- else
- [buttons_ addObject:UCLocalize("REINSTALL")];
- if (![package_ uninstalled])
- [buttons_ addObject:UCLocalize("REMOVE")];
- }
+ [alert setMessage:
+ @"Copyright \u00a9 2008-2011\n"
+ "SaurikIT, LLC\n"
+ "\n"
+ "Jay Freeman (saurik)\n"
+ "saurik@saurik.com\n"
+ "http://www.saurik.com/"
+ ];
- NSString *title;
- switch ([buttons_ count]) {
- case 0: title = nil; break;
- case 1: title = [buttons_ objectAtIndex:0]; break;
- default: title = UCLocalize("MODIFY"); break;
- }
+ [alert show];
+}
- button_ = [[[UIBarButtonItem alloc]
- initWithTitle:title
+- (UIBarButtonItem *) leftButton {
+ return [[[UIBarButtonItem alloc]
+ initWithTitle:UCLocalize("ABOUT")
style:UIBarButtonItemStylePlain
target:self
- action:@selector(customButtonClicked)
+ action:@selector(aboutButtonClicked)
] autorelease];
}
-- (bool) isLoading {
- return commercial_ ? [super isLoading] : false;
-}
-
@end
/* }}} */
-/* Package List Controller {{{ */
-
-// This is used to sort the filters. We want this to ensure that the
-// cheapest filters run first. This is probably a premature optimization.
-typedef enum {
- kPackageListFilterPriorityLow,
- kPackageListFilterPriorityNormal,
- kPackageListFilterPriorityHigh
-} PackageListFilterPriority;
+/* Refresh Bar {{{ */
+@interface RefreshBar : UINavigationBar {
+ _H<UIProgressIndicator> indicator_;
+ _H<UITextLabel> prompt_;
+ _H<UIProgressBar> progress_;
+ _H<UINavigationButton> cancel_;
+}
-typedef struct {
- SEL selector;
- id object;
- PackageListFilterPriority priority;
- IMP implementation;
-} PackageListFilter;
+@end
-@interface NSValue(PackageListFilter)
+@implementation RefreshBar
-+ (id)valueWithPackageListFilter:(PackageListFilter)filter;
-- (PackageListFilter)packageListFilterValue;
+- (void) positionViews {
+ CGRect frame = [cancel_ frame];
+ frame.size = [cancel_ sizeThatFits:frame.size];
+ frame.origin.x = [self frame].size.width - frame.size.width - 5;
+ frame.origin.y = ([self frame].size.height - frame.size.height) / 2;
+ [cancel_ setFrame:frame];
-@end
+ CGSize prgsize = {75, 100};
+ CGRect prgrect = {{
+ [self frame].size.width - prgsize.width - 10,
+ ([self frame].size.height - prgsize.height) / 2
+ } , prgsize};
+ [progress_ setFrame:prgrect];
-@implementation NSValue(PackageListFilter)
+ CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]);
+ unsigned indoffset = ([self frame].size.height - indsize.height) / 2;
+ CGRect indrect = {{indoffset, indoffset}, indsize};
+ [indicator_ setFrame:indrect];
-+ (id)valueWithPackageListFilter:(PackageListFilter)filter {
- return [NSValue valueWithBytes:&filter objCType:@encode(PackageListFilter)];
+ CGSize prmsize = {215, indsize.height + 4};
+ CGRect prmrect = {{
+ indoffset * 2 + indsize.width,
+ unsigned([self frame].size.height - prmsize.height) / 2 - 1
+ }, prmsize};
+ [prompt_ setFrame:prmrect];
}
-- (PackageListFilter)packageListFilterValue {
- PackageListFilter filter;
- [self getValue:&filter];
- return filter;
+- (void) setFrame:(CGRect)frame {
+ [super setFrame:frame];
+ [self positionViews];
}
-@end
-
-@interface FilteredPackageListDataSource : NSObject <UITableViewDataSource> {
- _transient Database *database_;
- unsigned era_;
- _H<NSArray> packages_;
- _H<NSMutableArray> sections_;
- _H<NSMutableArray> index_;
- _H<NSMutableDictionary> indices_;
- _H<NSMutableDictionary> filters_;
-}
+- (id) initWithFrame:(CGRect)frame delegate:(id)delegate {
+ if ((self = [super initWithFrame:frame]) != nil) {
+ [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
-- (id)objectForFilter:(NSString *)filter;
-- (void)setObject:(id)object forFilter:(NSString *)filter;
-- (SEL)selectorForFilter:(NSString *)filter;
-- (void)setSelector:(SEL)selector forFilter:(NSString *)filter;
-- (PackageListFilterPriority)priorityForFilter:(NSString *)filter;
-- (void)setPriority:(PackageListFilterPriority)priority forFilter:(NSString *)filter;
+ [self setBarStyle:UIBarStyleBlack];
-- (void)addFilter:(NSString *)filter withSelector:(SEL)selector;
-- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority;
-- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority object:(id)object;
-- (void)removeFilter:(NSString *)filter;
+ UIBarStyle barstyle([self _barStyle:NO]);
+ bool ugly(barstyle == UIBarStyleDefault);
-@end
+ UIProgressIndicatorStyle style = ugly ?
+ UIProgressIndicatorStyleMediumBrown :
+ UIProgressIndicatorStyleMediumWhite;
-@implementation FilteredPackageListDataSource
+ indicator_ = [[[UIProgressIndicator alloc] initWithFrame:CGRectZero] autorelease];
+ [(UIProgressIndicator *) indicator_ setStyle:style];
+ [indicator_ startAnimation];
+ [self addSubview:indicator_];
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super init]) != nil) {
- database_ = database;
- filters_ = [NSMutableDictionary dictionary];
- } return self;
-}
+ prompt_ = [[[UITextLabel alloc] initWithFrame:CGRectZero] autorelease];
+ [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
+ [prompt_ setBackgroundColor:[UIColor clearColor]];
+ [prompt_ setFont:[UIFont systemFontOfSize:15]];
+ [self addSubview:prompt_];
-+ (BOOL) supportsSearch {
- return YES;
-}
+ progress_ = [[[UIProgressBar alloc] initWithFrame:CGRectZero] autorelease];
+ [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin];
+ [(UIProgressBar *) progress_ setStyle:0];
+ [self addSubview:progress_];
-- (Package *) packageAtIndexPath:(NSIndexPath *)path {
-@synchronized (database_) {
- if ([database_ era] != era_)
- return nil;
+ cancel_ = [[[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted] autorelease];
+ [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+ [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside];
+ [cancel_ setBarStyle:barstyle];
- Section *section([sections_ objectAtIndex:[path section]]);
- NSInteger row([path row]);
- Package *package([packages_ objectAtIndex:([section row] + row)]);
- return [[package retain] autorelease];
-} }
+ [self positionViews];
+ } return self;
+}
-- (NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView {
- if ([[self class] supportsSearch])
- return [[NSArray arrayWithObject:UITableViewIndexSearch] arrayByAddingObjectsFromArray:index_];
+- (void) setCancellable:(bool)cancellable {
+ if (cancellable)
+ [self addSubview:cancel_];
else
- return index_;
+ [cancel_ removeFromSuperview];
}
-- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
- PackageCell *cell((PackageCell *) [table dequeueReusableCellWithIdentifier:@"Package"]);
- if (cell == nil)
- cell = [[[PackageCell alloc] init] autorelease];
-
- Package *package([database_ packageWithName:[[self packageAtIndexPath:path] id]]);
- [cell setPackage:package];
- return cell;
+- (void) start {
+ [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
+ [progress_ setProgress:0];
}
-- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
- NSInteger count([sections_ count]);
- return count == 0 ? 1 : count;
+- (void) stop {
+ [self setCancellable:NO];
}
-- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
- if ([sections_ count] == 0 || [[sections_ objectAtIndex:section] count] == 0)
- return nil;
- return [[sections_ objectAtIndex:section] name];
+- (void) setPrompt:(NSString *)prompt {
+ [prompt_ setText:prompt];
}
-- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
- if ([sections_ count] == 0)
- return 0;
- return [[sections_ objectAtIndex:section] count];
+- (void) setProgress:(float)progress {
+ [progress_ setProgress:progress];
}
-- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
- if ([[self class] supportsSearch]) {
- if (index == 0) {
- [tableView setContentOffset:CGPointZero animated:NO];
- return NSNotFound;
- } else {
- return index - 1;
- }
- }
+@end
+/* }}} */
- return index;
-}
+/* Cydia Navigation Controller Interface {{{ */
+@interface UINavigationController (Cydia)
-- (IMP)implementationForSelector:(SEL)selector {
-@synchronized (self) {
- /* XXX: this is an unsafe optimization of doomy hell */
- Method method(class_getInstanceMethod([Package class], selector));
- _assert(method != NULL);
- IMP imp = method_getImplementation(method);
- _assert(imp != NULL);
+- (NSArray *) navigationURLCollection;
+- (void) unloadData;
- return imp;
-} }
+@end
+/* }}} */
-- (PackageListFilter)packageListFilterForFilter:(NSString *)filter {
- return [[filters_ objectForKey:filter] packageListFilterValue];
-}
+/* Cydia Tab Bar Controller {{{ */
+@interface CYTabBarController : UITabBarController <
+ UITabBarControllerDelegate,
+ ProgressDelegate
+> {
+ _transient Database *database_;
+ _H<RefreshBar, 1> refreshbar_;
-- (void)setPackageListFilter:(PackageListFilter)filter forFilter:(NSString *)filterName {
- [filters_ setObject:[NSValue valueWithPackageListFilter:filter] forKey:filterName];
-}
+ bool dropped_;
+ bool updating_;
+ // XXX: ok, "updatedelegate_"?...
+ _transient NSObject<CydiaDelegate> *updatedelegate_;
-- (id)objectForFilter:(NSString *)filter {
- return [self packageListFilterForFilter:filter].object;
+ _H<UIViewController> remembered_;
+ _transient UIViewController *transient_;
}
-- (SEL)selectorForFilter:(NSString *)filter {
- return [self packageListFilterForFilter:filter].selector;
-}
+- (NSArray *) navigationURLCollection;
+- (void) dropBar:(BOOL)animated;
+- (void) beginUpdate;
+- (void) raiseBar:(BOOL)animated;
+- (BOOL) updating;
+- (void) unloadData;
-- (IMP)implementationForFilter:(NSString *)filter {
- return [self packageListFilterForFilter:filter].implementation;
-}
+@end
-- (PackageListFilterPriority)priorityForFilter:(NSString *)filter {
- return [self packageListFilterForFilter:filter].priority;
-}
+@implementation CYTabBarController
-- (void)setObject:(id)object forFilter:(NSString *)filterName {
- PackageListFilter filter = [self packageListFilterForFilter:filterName];
- filter.object = object;
- [self setPackageListFilter:filter forFilter:filterName];
+- (void) didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+
+ // presenting a UINavigationController on 2.x does not update its transitionView
+ // it thereby will not allow its topViewController to be unloaded by memory pressure
+ if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
+ UIViewController *selected([self selectedViewController]);
+ for (UINavigationController *controller in [self viewControllers])
+ if (controller != selected)
+ if (UIViewController *top = [controller topViewController])
+ [top unloadView];
+ }
}
-- (void)setPriority:(PackageListFilterPriority)priority forFilter:(NSString *)filterName {
- PackageListFilter filter = [self packageListFilterForFilter:filterName];
- filter.priority = priority;
- [self setPackageListFilter:filter forFilter:filterName];
+- (void) setUnselectedViewController:(UIViewController *)transient {
+ if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
+ if (transient != nil) {
+ [[[self viewControllers] objectAtIndex:0] pushViewController:transient animated:YES];
+ [self setSelectedIndex:0];
+ } return;
+ }
+
+ NSMutableArray *controllers = [[[self viewControllers] mutableCopy] autorelease];
+ if (transient != nil) {
+ UINavigationController *navigation([[[UINavigationController alloc] init] autorelease]);
+ [navigation setViewControllers:[NSArray arrayWithObject:transient]];
+ transient = navigation;
+
+ if (transient_ == nil)
+ remembered_ = [controllers objectAtIndex:0];
+ transient_ = transient;
+ [transient_ setTabBarItem:[remembered_ tabBarItem]];
+ [controllers replaceObjectAtIndex:0 withObject:transient_];
+ [self setSelectedIndex:0];
+ [self setViewControllers:controllers];
+ [self concealTabBarSelection];
+ } else if (remembered_ != nil) {
+ [remembered_ setTabBarItem:[transient_ tabBarItem]];
+ transient_ = transient;
+ [controllers replaceObjectAtIndex:0 withObject:remembered_];
+ remembered_ = nil;
+ [self setViewControllers:controllers];
+ [self revealTabBarSelection];
+ }
}
-- (void)setImplementation:(IMP)implementation forFilter:(NSString *)filterName {
- PackageListFilter filter = [self packageListFilterForFilter:filterName];
- filter.implementation = implementation;
- [self setPackageListFilter:filter forFilter:filterName];
+- (UIViewController *) unselectedViewController {
+ return transient_;
}
-- (void)setSelector:(SEL)selector forFilter:(NSString *)filterName {
- PackageListFilter filter = [self packageListFilterForFilter:filterName];
- filter.selector = selector;
- [self setPackageListFilter:filter forFilter:filterName];
+- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
+ if ([self unselectedViewController])
+ [self setUnselectedViewController:nil];
- [self setImplementation:[self implementationForSelector:selector] forFilter:filterName];
+ // presenting a UINavigationController on 2.x does not update its transitionView
+ // if this view was unloaded, the tranitionView may currently be presenting nothing
+ if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
+ UINavigationController *navigation((UINavigationController *) viewController);
+ [navigation pushViewController:[[[UIViewController alloc] init] autorelease] animated:NO];
+ [navigation popViewControllerAnimated:NO];
+ }
}
-- (void)addFilter:(NSString *)filterName {
- PackageListFilter filter;
- filter.object = nil;
- filter.selector = NULL;
- filter.implementation = NULL;
- filter.priority = kPackageListFilterPriorityNormal;
+- (NSArray *) navigationURLCollection {
+ NSMutableArray *items([NSMutableArray array]);
- [self setPackageListFilter:filter forFilter:filterName];
+ // XXX: Should this deal with transient view controllers?
+ for (id navigation in [self viewControllers]) {
+ NSArray *stack = [navigation performSelector:@selector(navigationURLCollection)];
+ if (stack != nil)
+ [items addObject:stack];
+ }
+
+ return items;
}
-- (void)addFilter:(NSString *)filter withSelector:(SEL)selector {
- [self addFilter:filter];
- [self setSelector:selector forFilter:filter];
+- (void) dismissModalViewControllerAnimated:(BOOL)animated {
+ if ([self modalViewController] == nil && [self unselectedViewController] != nil)
+ [self setUnselectedViewController:nil];
+ else
+ [super dismissModalViewControllerAnimated:YES];
}
-- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority {
- [self addFilter:filter withSelector:selector];
- [self setPriority:priority forFilter:filter];
+- (void) unloadData {
+ [super unloadData];
+
+ for (UINavigationController *controller in [self viewControllers])
+ [controller unloadData];
+
+ if (UIViewController *selected = [self selectedViewController])
+ [selected reloadData];
+
+ if (UIViewController *unselected = [self unselectedViewController]) {
+ [unselected unloadData];
+ [unselected reloadData];
+ }
}
-- (void)addFilter:(NSString *)filter withSelector:(SEL)selector priority:(PackageListFilterPriority)priority object:(id)object {
- [self addFilter:filter withSelector:selector];
- [self setObject:object forFilter:filter];
-}
+- (void) dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
-- (void)removeFilter:(NSString *)filter {
- [filters_ removeObjectForKey:filter];
+ [super dealloc];
}
-- (NSMutableArray *) _reloadPackages {
-@synchronized (database_) {
- era_ = [database_ era];
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super init]) != nil) {
+ database_ = database;
+ [self setDelegate:self];
- NSArray *packages([database_ packages]);
- NSMutableArray *filtered;
+ [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
- NSArray *filters = [filters_ allKeys];
+ refreshbar_ = [[[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self] autorelease];
+ } return self;
+}
- for (PackageListFilterPriority currentPriority = kPackageListFilterPriorityHigh; currentPriority >= kPackageListFilterPriorityLow; currentPriority = static_cast<PackageListFilterPriority>(static_cast<int>(currentPriority) - 1)) {
- for (NSString *filterName in filters) {
- PackageListFilter filter = [self packageListFilterForFilter:filterName];
+- (void) setUpdate:(NSDate *)date {
+ [self beginUpdate];
+}
- if (filter.priority == currentPriority) {
- filtered = [NSMutableArray arrayWithCapacity:[packages count]];
+- (void) beginUpdate {
+ [(RefreshBar *) refreshbar_ start];
+ [self dropBar:YES];
- IMP implementation;
- SEL selector;
- _H<NSObject> object;
+ [updatedelegate_ retainNetworkActivityIndicator];
+ updating_ = true;
- @synchronized (self) {
- implementation = filter.implementation;
- selector = filter.selector;
- object = filter.object;
- }
+ [NSThread
+ detachNewThreadSelector:@selector(performUpdate)
+ toTarget:self
+ withObject:nil
+ ];
+}
- if (implementation == NULL) continue;
+- (void) performUpdate {
+ NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
- _profile(PackageTable$reloadData$Filter)
- for (Package *package in packages)
- if ([package valid] && (*reinterpret_cast<bool (*)(id, SEL, id)>(implementation))(package, selector, object))
- [filtered addObject:package];
- _end
+ Status status;
+ status.setDelegate(self);
+ [database_ updateWithStatus:status];
- packages = filtered;
- }
- }
- }
+ [self
+ performSelectorOnMainThread:@selector(completeUpdate)
+ withObject:nil
+ waitUntilDone:NO
+ ];
- // packages would also be valid here, but it's defined
- // as an immutable array. filtered works too, so use it.
- return filtered;
-} }
+ [pool release];
+}
-- (void) reloadData {
- NSArray *packages;
+- (void) stopUpdateWithSelector:(SEL)selector {
+ updating_ = false;
+ [updatedelegate_ releaseNetworkActivityIndicator];
- reload:
- packages = [self _reloadPackages];
+ [self raiseBar:YES];
+ [refreshbar_ stop];
-@synchronized (database_) {
- if (era_ != [database_ era])
- goto reload;
+ [updatedelegate_ performSelector:selector withObject:nil afterDelay:0];
+}
- packages_ = packages;
+- (void) completeUpdate {
+ if (!updating_)
+ return;
+ [self stopUpdateWithSelector:@selector(reloadData)];
+}
- indices_ = [NSMutableDictionary dictionaryWithCapacity:32];
- sections_ = [NSMutableArray arrayWithCapacity:16];
+- (void) cancelUpdate {
+ [self stopUpdateWithSelector:@selector(updateDataAndLoad)];
+}
- Section *section = nil;
- index_ = [NSMutableArray arrayWithCapacity:32];
+- (void) cancelPressed {
+ [self cancelUpdate];
+}
- _profile(PackageTable$reloadData$Section)
- for (size_t offset(0), end([packages_ count]); offset != end; ++offset) {
- Package *package;
- unichar index;
+- (BOOL) updating {
+ return updating_;
+}
- _profile(PackageTable$reloadData$Section$Package)
- package = [packages_ objectAtIndex:offset];
- index = [package index];
- _end
+- (void) addProgressEvent:(CydiaProgressEvent *)event {
+ [refreshbar_ setPrompt:[event compoundMessage]];
+}
- if (section == nil || [section index] != index) {
- _profile(PackageTable$reloadData$Section$Allocate)
- section = [[[Section alloc] initWithIndex:index row:offset] autorelease];
- _end
+- (bool) isProgressCancelled {
+ return !updating_;
+}
- [index_ addObject:[section name]];
- //[indices_ setObject:[NSNumber numberForInt:[sections_ count]] forKey:index];
+- (void) setProgressCancellable:(NSNumber *)cancellable {
+ [refreshbar_ setCancellable:(updating_ && [cancellable boolValue])];
+}
- _profile(PackageTable$reloadData$Section$Add)
- [sections_ addObject:section];
- _end
- }
+- (void) setProgressPercent:(NSNumber *)percent {
+ [refreshbar_ setProgress:[percent floatValue]];
+}
- [section addToCount];
- }
- _end
-} }
+- (void) setProgressStatus:(NSDictionary *)status {
+ if (status != nil)
+ [self setProgressPercent:[status objectForKey:@"Percent"]];
+}
-@end
+- (void) setUpdateDelegate:(id)delegate {
+ updatedelegate_ = delegate;
+}
-@interface FilteredPackageListController : CyteViewController <UITableViewDelegate, UISearchDisplayDelegate> {
- _transient Database *database_;
- _H<UISearchDisplayController> searchController_;
- _H<UISearchBar> searchBar_;
- _H<FilteredPackageListDataSource> datasource_;
- _H<UITableView, 2> list_;
- _H<NSString> title_;
+- (UIView *) transitionView {
+ if ([self respondsToSelector:@selector(_transitionView)])
+ return [self _transitionView];
+ else
+ return MSHookIvar<id>(self, "_viewControllerTransitionView");
}
-- (id) initWithDatabase:(Database *)database title:(NSString *)title;
-- (void) clearData;
+- (void) dropBar:(BOOL)animated {
+ if (dropped_)
+ return;
+ dropped_ = true;
-@end
+ UIView *transition([self transitionView]);
+ [[self view] addSubview:refreshbar_];
-@implementation FilteredPackageListController
+ CGRect barframe([refreshbar_ frame]);
-- (NSURL *) referrerURL {
- return [self navigationURL];
-}
+ if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) // XXX: _UIApplicationLinkedOnOrAfter(4)
+ barframe.origin.y = CYStatusBarHeight();
+ else
+ barframe.origin.y = 0;
-- (void) deselectWithAnimation:(BOOL)animated {
- [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
-}
+ [refreshbar_ setFrame:barframe];
-- (void) viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- [self deselectWithAnimation:animated];
-}
+ if (animated)
+ [UIView beginAnimations:nil context:NULL];
-- (void) didSelectPackage:(Package *)package {
- CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[[self referrerURL] absoluteString]] autorelease]);
- [view setDelegate:delegate_];
- [[self navigationController] pushViewController:view animated:YES];
-}
+ CGRect viewframe = [transition frame];
+ viewframe.origin.y += barframe.size.height;
+ viewframe.size.height -= barframe.size.height;
+ [transition setFrame:viewframe];
-- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
- FilteredPackageListDataSource *source = datasource_;
+ if (animated)
+ [UIView commitAnimations];
- Package *package([source packageAtIndexPath:path]);
- package = [database_ packageWithName:[package id]];
- [self didSelectPackage:package];
+ // Ensure bar has the proper width for our view, it might have changed
+ barframe.size.width = viewframe.size.width;
+ [refreshbar_ setFrame:barframe];
}
-+ (Class) dataSourceClass {
- return [FilteredPackageListDataSource class];
-}
+- (void) raiseBar:(BOOL)animated {
+ if (!dropped_)
+ return;
+ dropped_ = false;
-- (id) initWithDatabase:(Database *)database title:(NSString *)title {
- if ((self = [super init]) != nil) {
- database_ = database;
+ UIView *transition([self transitionView]);
+ [refreshbar_ removeFromSuperview];
- datasource_ = [[[[[self class] dataSourceClass] alloc] initWithDatabase:database_] autorelease];
+ CGRect barframe([refreshbar_ frame]);
- title_ = [title copy];
- [[self navigationItem] setTitle:title_];
- } return self;
-}
+ if (animated)
+ [UIView beginAnimations:nil context:NULL];
-- (void) loadView {
- UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]);
- [view setBackgroundColor:[UIColor whiteColor]];
- [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
- [self setView:view];
+ CGRect viewframe = [transition frame];
+ viewframe.origin.y -= barframe.size.height;
+ viewframe.size.height += barframe.size.height;
+ [transition setFrame:viewframe];
- list_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStylePlain] autorelease];
- [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [view addSubview:list_];
+ if (animated)
+ [UIView commitAnimations];
+}
- // XXX: is 20 the most optimal number here?
- [list_ setSectionIndexMinimumDisplayRowCount:20];
- [list_ setRowHeight:73];
- [(UITableView *) list_ setDataSource:datasource_];
- [list_ setDelegate:self];
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+ bool dropped(dropped_);
+
+ if (dropped)
+ [self raiseBar:NO];
- if ([[[self class] dataSourceClass] supportsSearch]) {
- searchBar_ = [[UISearchBar alloc] init];
- [searchBar_ sizeToFit];
- searchController_ = [[UISearchDisplayController alloc] initWithSearchBar:searchBar_ contentsController:self];
- [searchController_ setDelegate:self];
- [list_ setTableHeaderView:searchBar_];
+ [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
- [searchController_ setSearchResultsDataSource:datasource_];
- [searchController_ setSearchResultsDelegate:self];
- }
+ if (dropped)
+ [self dropBar:NO];
}
-- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
- [datasource_ addFilter:@"search" withSelector:@selector(isUnfilteredAndSelectedForBy:) priority:kPackageListFilterPriorityLow object:searchString];
- [datasource_ reloadData];
- return YES;
+- (void) statusBarFrameChanged:(NSNotification *)notification {
+ if (dropped_) {
+ [self raiseBar:NO];
+ [self dropBar:NO];
+ }
}
-- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller {
- [datasource_ removeFilter:@"search"];
- [self reloadData];
-}
+@end
+/* }}} */
-- (void)searchDisplayController:(UISearchDisplayController *)controller willShowSearchResultsTableView:(UITableView *)tableView {
- // XXX: is 20 the most optimal number here?
- [tableView setSectionIndexMinimumDisplayRowCount:20];
- [tableView setRowHeight:73];
-}
+/* Cydia Navigation Controller Implementation {{{ */
+@implementation UINavigationController (Cydia)
-- (void) releaseSubviews {
- list_ = nil;
+- (NSArray *) navigationURLCollection {
+ NSMutableArray *stack([NSMutableArray array]);
- [super releaseSubviews];
+ for (CyteViewController *controller in [self viewControllers]) {
+ NSString *url = [[controller navigationURL] absoluteString];
+ if (url != nil)
+ [stack addObject:url];
+ }
+
+ return stack;
}
- (void) reloadData {
[super reloadData];
- [datasource_ reloadData];
- [list_ reloadData];
-}
+ UIViewController *visible([self visibleViewController]);
+ if (visible != nil)
+ [visible reloadData];
-- (void) resetScrollPosition {
- [list_ scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
+ // on the iPad, this view controller is ALSO visible. :(
+ if (IsWildcat_)
+ if (UIViewController *top = [self topViewController])
+ if (top != visible)
+ [top reloadData];
}
-- (void) clearData {
- [list_ setDataSource:nil];
- [list_ reloadData];
+- (void) unloadData {
+ for (CyteViewController *page in [self viewControllers])
+ [page unloadData];
- [self resetScrollPosition];
+ [super unloadData];
}
@end
/* }}} */
-/* Home Controller {{{ */
-@interface HomeController : CydiaWebViewController {
- CFRunLoopRef runloop_;
- SCNetworkReachabilityRef reachability_;
+/* Cydia:// Protocol {{{ */
+@interface CydiaURLProtocol : NSURLProtocol {
}
@end
-@implementation HomeController
-
-static void HomeControllerReachabilityCallback(SCNetworkReachabilityRef reachability, SCNetworkReachabilityFlags flags, void *info) {
- [(HomeController *) info dispatchEvent:@"CydiaReachabilityCallback"];
-}
+@implementation CydiaURLProtocol
-- (id) init {
- if ((self = [super init]) != nil) {
- [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/home/", UI_]]];
- [self reloadData];
++ (BOOL) canInitWithRequest:(NSURLRequest *)request {
+ NSURL *url([request URL]);
+ if (url == nil)
+ return NO;
- reachability_ = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, "cydia.saurik.com");
- if (reachability_ != NULL) {
- SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
- SCNetworkReachabilitySetCallback(reachability_, HomeControllerReachabilityCallback, &context);
+ NSString *scheme([[url scheme] lowercaseString]);
+ if (scheme != nil && [scheme isEqualToString:@"cydia"])
+ return YES;
+ if ([[url absoluteString] hasPrefix:@"about:cydia-"])
+ return YES;
- CFRunLoopRef runloop(CFRunLoopGetCurrent());
- if (SCNetworkReachabilityScheduleWithRunLoop(reachability_, runloop, kCFRunLoopDefaultMode))
- runloop_ = runloop;
- }
- } return self;
+ return NO;
}
-- (void) dealloc {
- if (reachability_ != NULL && runloop_ != NULL)
- SCNetworkReachabilityUnscheduleFromRunLoop(reachability_, runloop_, kCFRunLoopDefaultMode);
- [super dealloc];
++ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
+ return request;
}
-- (NSURL *) navigationURL {
- return [NSURL URLWithString:@"cydia://home"];
+- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
+ id<NSURLProtocolClient> client([self client]);
+ if (icon == nil)
+ [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
+ else {
+ NSData *data(UIImagePNGRepresentation(icon));
+
+ NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
+ [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ [client URLProtocol:self didLoadData:data];
+ [client URLProtocolDidFinishLoading:self];
+ }
}
-- (void) aboutButtonClicked {
- UIAlertView *alert([[[UIAlertView alloc] init] autorelease]);
+- (void) startLoading {
+ id<NSURLProtocolClient> client([self client]);
+ NSURLRequest *request([self request]);
- [alert setTitle:UCLocalize("ABOUT_CYDIA")];
- [alert addButtonWithTitle:UCLocalize("CLOSE")];
- [alert setCancelButtonIndex:0];
+ NSURL *url([request URL]);
+ NSString *href([url absoluteString]);
+ NSString *scheme([[url scheme] lowercaseString]);
- [alert setMessage:
- @"Copyright \u00a9 2008-2011\n"
- "SaurikIT, LLC\n"
- "\n"
- "Jay Freeman (saurik)\n"
- "saurik@saurik.com\n"
- "http://www.saurik.com/"
- ];
+ NSString *path;
- [alert show];
+ if ([scheme isEqualToString:@"cydia"])
+ path = [href substringFromIndex:8];
+ else if ([scheme isEqualToString:@"about"])
+ path = [href substringFromIndex:12];
+ else _assert(false);
+
+ NSRange slash([path rangeOfString:@"/"]);
+
+ NSString *command;
+ if (slash.location == NSNotFound) {
+ command = path;
+ path = nil;
+ } else {
+ command = [path substringToIndex:slash.location];
+ path = [path substringFromIndex:(slash.location + 1)];
+ }
+
+ Database *database([Database sharedInstance]);
+
+ if ([command isEqualToString:@"package-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ Package *package([database packageWithName:path]);
+ if (package == nil)
+ goto fail;
+ [package parse];
+ UIImage *icon([package icon]);
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else if ([command isEqualToString:@"uikit-image"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ UIImage *icon(_UIImageWithName(path));
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else if ([command isEqualToString:@"section-icon"]) {
+ if (path == nil)
+ goto fail;
+ path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, [path stringByReplacingOccurrencesOfString:@" " withString:@"_"]]]);
+ if (icon == nil)
+ icon = [UIImage applicationImageNamed:@"unknown.png"];
+ [self _returnPNGWithImage:icon forRequest:request];
+ } else fail: {
+ [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
+ }
}
-- (UIBarButtonItem *) leftButton {
- return [[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("ABOUT")
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(aboutButtonClicked)
- ] autorelease];
+- (void) stopLoading {
}
@end
/* }}} */
-/* Refresh Bar {{{ */
-@interface RefreshBar : UINavigationBar {
- _H<UIProgressIndicator> indicator_;
- _H<UITextLabel> prompt_;
- _H<UIProgressBar> progress_;
- _H<UINavigationButton> cancel_;
+/* Section Controller {{{ */
+@interface SectionController : FilteredPackageListController {
+ _H<IndirectDelegate, 1> indirect_;
+ _H<CydiaObject> cydia_;
+ _H<NSString> section_;
+ _H<NSString> key_;
+ _transient Source *source_;
+ std::vector< _H<CyteWebViewTableViewCell, 1> > promoted_;
}
-@end
+- (id) initWithDatabase:(Database *)database section:(NSString *)section source:(Source *)source;
-@implementation RefreshBar
+@end
-- (void) positionViews {
- CGRect frame = [cancel_ frame];
- frame.size = [cancel_ sizeThatFits:frame.size];
- frame.origin.x = [self frame].size.width - frame.size.width - 5;
- frame.origin.y = ([self frame].size.height - frame.size.height) / 2;
- [cancel_ setFrame:frame];
+@implementation SectionController
- CGSize prgsize = {75, 100};
- CGRect prgrect = {{
- [self frame].size.width - prgsize.width - 10,
- ([self frame].size.height - prgsize.height) / 2
- } , prgsize};
- [progress_ setFrame:prgrect];
+- (NSURL *) referrerURL {
+ NSMutableString *path = [NSMutableString string];
- CGSize indsize([UIProgressIndicator defaultSizeForStyle:[indicator_ activityIndicatorViewStyle]]);
- unsigned indoffset = ([self frame].size.height - indsize.height) / 2;
- CGRect indrect = {{indoffset, indoffset}, indsize};
- [indicator_ setFrame:indrect];
+ if (source_ != nil) {
+ [path appendString:[NSString stringWithFormat:@"sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
+ } else {
+ [path appendString:@"sections"];
+ }
- CGSize prmsize = {215, indsize.height + 4};
- CGRect prmrect = {{
- indoffset * 2 + indsize.width,
- unsigned([self frame].size.height - prmsize.height) / 2 - 1
- }, prmsize};
- [prompt_ setFrame:prmrect];
-}
+ [path appendString:@"/"];
-- (void) setFrame:(CGRect)frame {
- [super setFrame:frame];
- [self positionViews];
+ if (section_ != nil) {
+ [path appendString:[NSString stringWithFormat:@"%@", [section_ stringByAddingPercentEscapesIncludingReserved]]];
+ } else {
+ [path appendString:@"all"];
+ }
+
+ return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/%@", UI_, path]];
}
-- (id) initWithFrame:(CGRect)frame delegate:(id)delegate {
- if ((self = [super initWithFrame:frame]) != nil) {
- [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
+- (NSURL *) navigationURL {
+ NSMutableString *path = [NSMutableString string];
- [self setBarStyle:UIBarStyleBlack];
+ if (source_ != nil) {
+ [path appendString:[NSString stringWithFormat:@"sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
+ } else {
+ [path appendString:@"sections"];
+ }
- UIBarStyle barstyle([self _barStyle:NO]);
- bool ugly(barstyle == UIBarStyleDefault);
+ [path appendString:@"/"];
- UIProgressIndicatorStyle style = ugly ?
- UIProgressIndicatorStyleMediumBrown :
- UIProgressIndicatorStyleMediumWhite;
+ if (section_ != nil) {
+ [path appendString:[NSString stringWithFormat:@"%@", [section_ stringByAddingPercentEscapesIncludingReserved]]];
+ } else {
+ [path appendString:@"all"];
+ }
- indicator_ = [[[UIProgressIndicator alloc] initWithFrame:CGRectZero] autorelease];
- [(UIProgressIndicator *) indicator_ setStyle:style];
- [indicator_ startAnimation];
- [self addSubview:indicator_];
+ return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://%@", path]];
+}
- prompt_ = [[[UITextLabel alloc] initWithFrame:CGRectZero] autorelease];
- [prompt_ setColor:[UIColor colorWithCGColor:(ugly ? Blueish_ : Off_)]];
- [prompt_ setBackgroundColor:[UIColor clearColor]];
- [prompt_ setFont:[UIFont systemFontOfSize:15]];
- [self addSubview:prompt_];
+- (id) initWithDatabase:(Database *)database section:(NSString *)name source:(Source *)source {
+ NSString *title;
+ if (name == nil)
+ title = UCLocalize("ALL_PACKAGES");
+ else if (![name isEqual:@""])
+ title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
+ else
+ title = UCLocalize("NO_SECTION");
- progress_ = [[[UIProgressBar alloc] initWithFrame:CGRectZero] autorelease];
- [progress_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin];
- [(UIProgressBar *) progress_ setStyle:0];
- [self addSubview:progress_];
+ if ((self = [super initWithDatabase:database title:title]) != nil) {
+ indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
+ cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease];
- cancel_ = [[[UINavigationButton alloc] initWithTitle:UCLocalize("CANCEL") style:UINavigationButtonStyleHighlighted] autorelease];
- [cancel_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
- [cancel_ addTarget:delegate action:@selector(cancelPressed) forControlEvents:UIControlEventTouchUpInside];
- [cancel_ setBarStyle:barstyle];
+ [datasource_ addFilter:@"section" withSelector:@selector(isVisibleInSection:) priority:kPackageListFilterPriorityHigh object:name];
+ [datasource_ addFilter:@"source" withSelector:@selector(isVisibleInSource:) priority:kPackageListFilterPriorityHigh object:source];
- [self positionViews];
+ section_ = name;
+ source_ = source;
+ key_ = [source key];
} return self;
}
-- (void) setCancellable:(bool)cancellable {
- if (cancellable)
- [self addSubview:cancel_];
- else
- [cancel_ removeFromSuperview];
+/*- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
+ return [super numberOfSectionsInTableView:list] + 1;
}
-- (void) start {
- [prompt_ setText:UCLocalize("UPDATING_DATABASE")];
- [progress_ setProgress:0];
+- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
+ return section == 0 ? nil : [super tableView:list titleForHeaderInSection:(section - 1)];
}
-- (void) stop {
- [self setCancellable:NO];
+- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
+ return section == 0 ? promoted_.size() : [super tableView:list numberOfRowsInSection:(section - 1)];
}
-- (void) setPrompt:(NSString *)prompt {
- [prompt_ setText:prompt];
++ (NSIndexPath *) adjustedIndexPath:(NSIndexPath *)path {
+ return [NSIndexPath indexPathForRow:[path row] inSection:([path section] - 1)];
}
-- (void) setProgress:(float)progress {
- [progress_ setProgress:progress];
+- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
+ if ([path section] != 0)
+ return [super tableView:table cellForRowAtIndexPath:[SectionController adjustedIndexPath:path]];
+
+ return promoted_[[path row]];
}
-@end
-/* }}} */
+- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
+ if ([path section] != 0)
+ return [super tableView:table didSelectRowAtIndexPath:[SectionController adjustedIndexPath:path]];
+}
-/* Cydia Navigation Controller Interface {{{ */
-@interface UINavigationController (Cydia)
+- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
+ NSInteger section([super tableView:tableView sectionForSectionIndexTitle:title atIndex:index]);
+ return section == 0 ? 0 : section + 1;
+}*/
-- (NSArray *) navigationURLCollection;
-- (void) unloadData;
+- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
+ NSURL *url([request URL]);
+ if (url == nil)
+ return;
-@end
-/* }}} */
+ if ([frame isEqualToString:@"_open"])
+ [delegate_ openURL:url];
+ else {
+ WebFrame *frame(nil);
+ if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
+ frame = [WebActionElement objectForKey:@"WebElementFrame"];
+ if (frame == nil)
+ frame = [view mainFrame];
-/* Cydia Tab Bar Controller {{{ */
-@interface CYTabBarController : UITabBarController <
- UITabBarControllerDelegate,
- ProgressDelegate
-> {
- _transient Database *database_;
- _H<RefreshBar, 1> refreshbar_;
+ WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
- bool dropped_;
- bool updating_;
- // XXX: ok, "updatedelegate_"?...
- _transient NSObject<CydiaDelegate> *updatedelegate_;
+ CyteViewController *controller([delegate_ pageForURL:url forExternal:NO withReferrer:([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString])] ?: [[[CydiaWebViewController alloc] initWithRequest:request] autorelease]);
+ [controller setDelegate:delegate_];
+ [[self navigationController] pushViewController:controller animated:YES];
+ }
- _H<UIViewController> remembered_;
- _transient UIViewController *transient_;
+ [listener ignore];
}
-- (NSArray *) navigationURLCollection;
-- (void) dropBar:(BOOL)animated;
-- (void) beginUpdate;
-- (void) raiseBar:(BOOL)animated;
-- (BOOL) updating;
-- (void) unloadData;
-
-@end
+- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
+ return [CydiaWebViewController requestWithHeaders:request];
+}
-@implementation CYTabBarController
+- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+ [CydiaWebViewController didClearWindowObject:window forFrame:frame withCydia:cydia_];
+}
-- (void) didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
+- (void) loadView {
+ [super loadView];
- // presenting a UINavigationController on 2.x does not update its transitionView
- // it thereby will not allow its topViewController to be unloaded by memory pressure
- if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
- UIViewController *selected([self selectedViewController]);
- for (UINavigationController *controller in [self viewControllers])
- if (controller != selected)
- if (UIViewController *top = [controller topViewController])
- [top unloadView];
- }
-}
+ // XXX: this code is horrible. I mean, wtf Jay?
+ if (ShowPromoted_ && [[Metadata_ objectForKey:@"ShowPromoted"] boolValue]) {
+ promoted_.resize(1);
-- (void) setUnselectedViewController:(UIViewController *)transient {
- if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
- if (transient != nil) {
- [[[self viewControllers] objectAtIndex:0] pushViewController:transient animated:YES];
- [self setSelectedIndex:0];
- } return;
- }
+ for (unsigned i(0); i != promoted_.size(); ++i) {
+ CyteWebViewTableViewCell *promoted([CyteWebViewTableViewCell cellWithRequest:[NSURLRequest
+ requestWithURL:[Diversion divertURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sectionhead/%u/%@",
+ UI_, i, section_ == nil ? @"" : [section_ stringByAddingPercentEscapesIncludingReserved]]
+ ]]
- NSMutableArray *controllers = [[[self viewControllers] mutableCopy] autorelease];
- if (transient != nil) {
- UINavigationController *navigation([[[UINavigationController alloc] init] autorelease]);
- [navigation setViewControllers:[NSArray arrayWithObject:transient]];
- transient = navigation;
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:120
+ ]]);
- if (transient_ == nil)
- remembered_ = [controllers objectAtIndex:0];
- transient_ = transient;
- [transient_ setTabBarItem:[remembered_ tabBarItem]];
- [controllers replaceObjectAtIndex:0 withObject:transient_];
- [self setSelectedIndex:0];
- [self setViewControllers:controllers];
- [self concealTabBarSelection];
- } else if (remembered_ != nil) {
- [remembered_ setTabBarItem:[transient_ tabBarItem]];
- transient_ = transient;
- [controllers replaceObjectAtIndex:0 withObject:remembered_];
- remembered_ = nil;
- [self setViewControllers:controllers];
- [self revealTabBarSelection];
+ [promoted setDelegate:self];
+ promoted_[i] = promoted;
+ }
}
}
-- (UIViewController *) unselectedViewController {
- return transient_;
+- (void) setDelegate:(id)delegate {
+ [super setDelegate:delegate];
+ [cydia_ setDelegate:delegate];
}
-- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
- if ([self unselectedViewController])
- [self setUnselectedViewController:nil];
-
- // presenting a UINavigationController on 2.x does not update its transitionView
- // if this view was unloaded, the tranitionView may currently be presenting nothing
- if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
- UINavigationController *navigation((UINavigationController *) viewController);
- [navigation pushViewController:[[[UIViewController alloc] init] autorelease] animated:NO];
- [navigation popViewControllerAnimated:NO];
- }
+- (void) releaseSubviews {
+ promoted_.clear();
+ [super releaseSubviews];
}
-- (NSArray *) navigationURLCollection {
- NSMutableArray *items([NSMutableArray array]);
-
- // XXX: Should this deal with transient view controllers?
- for (id navigation in [self viewControllers]) {
- NSArray *stack = [navigation performSelector:@selector(navigationURLCollection)];
- if (stack != nil)
- [items addObject:stack];
- }
+- (void)reloadData {
+ source_ = [database_ sourceWithKey:key_];
+ key_ = [source_ key];
+ [datasource_ setObject:source_ forFilter:@"source"];
- return items;
+ [super reloadData];
}
-- (void) dismissModalViewControllerAnimated:(BOOL)animated {
- if ([self modalViewController] == nil && [self unselectedViewController] != nil)
- [self setUnselectedViewController:nil];
- else
- [super dismissModalViewControllerAnimated:YES];
+@end
+/* }}} */
+/* Sections Controller {{{ */
+@interface SectionsController : CyteViewController <
+ UITableViewDataSource,
+ UITableViewDelegate
+> {
+ _transient Database *database_;
+ _H<NSMutableArray> sections_;
+ _H<NSMutableArray> filtered_;
+ _H<UITableView, 2> list_;
+ _H<NSString> key_;
+ _transient Source *source_;
}
-- (void) unloadData {
- [super unloadData];
+- (id) initWithDatabase:(Database *)database source:(Source *)source;
+- (void) editButtonClicked;
- for (UINavigationController *controller in [self viewControllers])
- [controller unloadData];
+@end
- if (UIViewController *selected = [self selectedViewController])
- [selected reloadData];
+@implementation SectionsController
- if (UIViewController *unselected = [self unselectedViewController]) {
- [unselected unloadData];
- [unselected reloadData];
+- (NSURL *) navigationURL {
+ if (source_ != nil) {
+ return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sections/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
+ } else {
+ return [NSURL URLWithString:@"cydia://sections"];
}
}
-- (void) dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
+- (NSString *)defaultTitle {
+ if (source_ != nil) {
+ return [source_ label];
+ } else {
+ return UCLocalize("SECTIONS");
+ }
+}
- [super dealloc];
+- (void) updateNavigationItem {
+ [[self navigationItem] setTitle:[self isEditing] ? UCLocalize("SECTION_VISIBILITY") : [self defaultTitle]];
+ if ([sections_ count] == 0) {
+ [[self navigationItem] setRightBarButtonItem:nil];
+ } else {
+ [[self navigationItem] setRightBarButtonItem:[[UIBarButtonItem alloc]
+ initWithBarButtonSystemItem:([self isEditing] ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit)
+ target:self
+ action:@selector(editButtonClicked)
+ ] animated:([[self navigationItem] rightBarButtonItem] != nil)];
+ }
}
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super init]) != nil) {
- database_ = database;
- [self setDelegate:self];
+- (void) setEditing:(BOOL)editing animated:(BOOL)animated {
+ [super setEditing:editing animated:animated];
- [[self view] setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarFrameChanged:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil];
+ if (editing)
+ [list_ reloadData];
+ else
+ [delegate_ updateData];
- refreshbar_ = [[[RefreshBar alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [UINavigationBar defaultSize].height) delegate:self] autorelease];
- } return self;
+ [self updateNavigationItem];
}
-- (void) setUpdate:(NSDate *)date {
- [self beginUpdate];
+- (void) viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
}
-- (void) beginUpdate {
- [(RefreshBar *) refreshbar_ start];
- [self dropBar:YES];
-
- [updatedelegate_ retainNetworkActivityIndicator];
- updating_ = true;
-
- [NSThread
- detachNewThreadSelector:@selector(performUpdate)
- toTarget:self
- withObject:nil
- ];
+- (void) viewWillDisappear:(BOOL)animated {
+ [super viewWillDisappear:animated];
+ [self setEditing:NO];
}
-- (void) performUpdate {
- NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
+- (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath {
+ Section *section = nil;
+ int index = [indexPath row];
+ if (![self isEditing]) {
+ index -= 1;
+ if (index >= 0)
+ section = [filtered_ objectAtIndex:index];
+ } else {
+ section = [sections_ objectAtIndex:index];
+ }
+ return section;
+}
- Status status;
- status.setDelegate(self);
- [database_ updateWithStatus:status];
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ if ([self isEditing])
+ return [sections_ count];
+ else
+ return [filtered_ count] + 1;
+}
- [self
- performSelectorOnMainThread:@selector(completeUpdate)
- withObject:nil
- waitUntilDone:NO
- ];
+/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
+ return 45.0f;
+}*/
- [pool release];
-}
+- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+ static NSString *reuseIdentifier = @"SectionCell";
-- (void) stopUpdateWithSelector:(SEL)selector {
- updating_ = false;
- [updatedelegate_ releaseNetworkActivityIndicator];
+ SectionCell *cell = (SectionCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
+ if (cell == nil)
+ cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
- [self raiseBar:YES];
- [refreshbar_ stop];
+ [cell setSection:[self sectionAtIndexPath:indexPath] editing:[self isEditing]];
- [updatedelegate_ performSelector:selector withObject:nil afterDelay:0];
+ return cell;
}
-- (void) completeUpdate {
- if (!updating_)
+- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+ if ([self isEditing])
return;
- [self stopUpdateWithSelector:@selector(reloadData)];
-}
-- (void) cancelUpdate {
- [self stopUpdateWithSelector:@selector(updateDataAndLoad)];
-}
+ Section *section = [self sectionAtIndexPath:indexPath];
-- (void) cancelPressed {
- [self cancelUpdate];
-}
+ SectionController *controller = [[[SectionController alloc]
+ initWithDatabase:database_
+ section:[section name]
+ source:source_
+ ] autorelease];
+ [controller setDelegate:delegate_];
-- (BOOL) updating {
- return updating_;
+ [[self navigationController] pushViewController:controller animated:YES];
}
-- (void) addProgressEvent:(CydiaProgressEvent *)event {
- [refreshbar_ setPrompt:[event compoundMessage]];
+- (void) loadView {
+ list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
+ [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [list_ setRowHeight:45.0f];
+ [(UITableView *) list_ setDataSource:self];
+ [list_ setDelegate:self];
+ [self setView:list_];
}
-- (bool) isProgressCancelled {
- return !updating_;
-}
+- (void) viewDidLoad {
+ [super viewDidLoad];
-- (void) setProgressCancellable:(NSNumber *)cancellable {
- [refreshbar_ setCancellable:(updating_ && [cancellable boolValue])];
+ [[self navigationItem] setTitle:[self defaultTitle]];
}
-- (void) setProgressPercent:(NSNumber *)percent {
- [refreshbar_ setProgress:[percent floatValue]];
-}
+- (void) releaseSubviews {
+ list_ = nil;
-- (void) setProgressStatus:(NSDictionary *)status {
- if (status != nil)
- [self setProgressPercent:[status objectForKey:@"Percent"]];
-}
+ sections_ = nil;
+ filtered_ = nil;
-- (void) setUpdateDelegate:(id)delegate {
- updatedelegate_ = delegate;
+ [super releaseSubviews];
}
-- (UIView *) transitionView {
- if ([self respondsToSelector:@selector(_transitionView)])
- return [self _transitionView];
- else
- return MSHookIvar<id>(self, "_viewControllerTransitionView");
+- (id) initWithDatabase:(Database *)database source:(Source *)source {
+ if ((self = [super init]) != nil) {
+ database_ = database;
+ source_ = source;
+ key_ = [source_ key];
+ } return self;
}
-- (void) dropBar:(BOOL)animated {
- if (dropped_)
- return;
- dropped_ = true;
-
- UIView *transition([self transitionView]);
- [[self view] addSubview:refreshbar_];
-
- CGRect barframe([refreshbar_ frame]);
-
- if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) // XXX: _UIApplicationLinkedOnOrAfter(4)
- barframe.origin.y = CYStatusBarHeight();
- else
- barframe.origin.y = 0;
-
- [refreshbar_ setFrame:barframe];
-
- if (animated)
- [UIView beginAnimations:nil context:NULL];
-
- CGRect viewframe = [transition frame];
- viewframe.origin.y += barframe.size.height;
- viewframe.size.height -= barframe.size.height;
- [transition setFrame:viewframe];
-
- if (animated)
- [UIView commitAnimations];
+- (void) reloadData {
+ source_ = [database_ sourceWithKey:key_];
+ key_ = [source_ key];
- // Ensure bar has the proper width for our view, it might have changed
- barframe.size.width = viewframe.size.width;
- [refreshbar_ setFrame:barframe];
-}
+ [[self navigationItem] setTitle:[source_ label]];
-- (void) raiseBar:(BOOL)animated {
- if (!dropped_)
- return;
- dropped_ = false;
+ [super reloadData];
- UIView *transition([self transitionView]);
- [refreshbar_ removeFromSuperview];
+ NSArray *packages = [database_ packages];
- CGRect barframe([refreshbar_ frame]);
+ sections_ = [NSMutableArray arrayWithCapacity:16];
+ filtered_ = [NSMutableArray arrayWithCapacity:16];
- if (animated)
- [UIView beginAnimations:nil context:NULL];
+ NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]);
- CGRect viewframe = [transition frame];
- viewframe.origin.y -= barframe.size.height;
- viewframe.size.height += barframe.size.height;
- [transition setFrame:viewframe];
+ _trace();
+ for (Package *package in packages) {
+ // Ignore packages from other sources (but allow all without a source).
+ if (source_ != nil && ![package isVisibleInSource:source_]) continue;
- if (animated)
- [UIView commitAnimations];
-}
+ NSString *name([package section]);
+ NSString *key(name == nil ? @"" : name);
-- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
- bool dropped(dropped_);
+ Section *section;
- if (dropped)
- [self raiseBar:NO];
+ _profile(SectionsView$reloadData$Section)
+ section = [sections objectForKey:key];
+ if (section == nil) {
+ _profile(SectionsView$reloadData$Section$Allocate)
+ section = [[[Section alloc] initWithName:key localize:YES] autorelease];
+ [sections setObject:section forKey:key];
+ _end
+ }
+ _end
- [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+ [section addToCount];
- if (dropped)
- [self dropBar:NO];
-}
+ _profile(SectionsView$reloadData$Filter)
+ if (![package valid] || ![package visible])
+ continue;
+ _end
-- (void) statusBarFrameChanged:(NSNotification *)notification {
- if (dropped_) {
- [self raiseBar:NO];
- [self dropBar:NO];
+ [section addToRow];
}
-}
+ _trace();
-@end
-/* }}} */
+ [sections_ addObjectsFromArray:[sections allValues]];
-/* Cydia Navigation Controller Implementation {{{ */
-@implementation UINavigationController (Cydia)
+ [sections_ sortUsingSelector:@selector(compareByLocalized:)];
-- (NSArray *) navigationURLCollection {
- NSMutableArray *stack([NSMutableArray array]);
+ for (Section *section in (id) sections_) {
+ size_t count([section row]);
+ if (count == 0)
+ continue;
- for (CyteViewController *controller in [self viewControllers]) {
- NSString *url = [[controller navigationURL] absoluteString];
- if (url != nil)
- [stack addObject:url];
+ section = [[[Section alloc] initWithName:[section name] localized:[section localized]] autorelease];
+ [section setCount:count];
+ [filtered_ addObject:section];
}
- return stack;
-}
-
-- (void) reloadData {
- [super reloadData];
-
- UIViewController *visible([self visibleViewController]);
- if (visible != nil)
- [visible reloadData];
-
- // on the iPad, this view controller is ALSO visible. :(
- if (IsWildcat_)
- if (UIViewController *top = [self topViewController])
- if (top != visible)
- [top reloadData];
+ [self updateNavigationItem];
+ [list_ reloadData];
+ _trace();
}
-- (void) unloadData {
- for (CyteViewController *page in [self viewControllers])
- [page unloadData];
-
- [super unloadData];
+- (void) editButtonClicked {
+ [self setEditing:![self isEditing] animated:YES];
}
@end
/* }}} */
-/* Cydia:// Protocol {{{ */
-@interface CydiaURLProtocol : NSURLProtocol {
+/* Changes Controller {{{ */
+@interface ChangesPackageListDataSource : FilteredPackageListDataSource {
+ unsigned upgrades_;
}
@end
-@implementation CydiaURLProtocol
+@implementation ChangesPackageListDataSource
-+ (BOOL) canInitWithRequest:(NSURLRequest *)request {
- NSURL *url([request URL]);
- if (url == nil)
- return NO;
+- (NSMutableArray *) _reloadPackages {
+@synchronized (database_) {
+ era_ = [database_ era];
+ NSArray *packages([database_ packages]);
- NSString *scheme([[url scheme] lowercaseString]);
- if (scheme != nil && [scheme isEqualToString:@"cydia"])
- return YES;
- if ([[url absoluteString] hasPrefix:@"about:cydia-"])
- return YES;
+ NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]);
- return NO;
-}
+ _trace();
+ _profile(ChangesPackageListController$_reloadPackages$Filter)
+ for (Package *package in packages)
+ if ([package upgradableAndEssential:YES] || [package visible])
+ CFArrayAppendValue((CFMutableArrayRef) filtered, package);
+ _end
+ _trace();
+ _profile(ChangesPackageListController$_reloadPackages$radixSort)
+ [filtered radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackageChangesRadix) withContext:NULL];
+ _end
+ _trace();
-+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
- return request;
-}
+ return filtered;
+} }
-- (void) _returnPNGWithImage:(UIImage *)icon forRequest:(NSURLRequest *)request {
- id<NSURLProtocolClient> client([self client]);
- if (icon == nil)
- [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]];
- else {
- NSData *data(UIImagePNGRepresentation(icon));
+- (void) reloadData {
+ NSArray *packages = [self _reloadPackages];
- NSURLResponse *response([[[NSURLResponse alloc] initWithURL:[request URL] MIMEType:@"image/png" expectedContentLength:-1 textEncodingName:nil] autorelease]);
- [client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
- [client URLProtocol:self didLoadData:data];
- [client URLProtocolDidFinishLoading:self];
- }
-}
+@synchronized (database_) {
+ packages_ = packages;
+ sections_ = [NSMutableArray arrayWithCapacity:16];
-- (void) startLoading {
- id<NSURLProtocolClient> client([self client]);
- NSURLRequest *request([self request]);
+ Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
+ Section *ignored = nil;
+ Section *section = nil;
+ time_t last = 0;
- NSURL *url([request URL]);
- NSString *href([url absoluteString]);
- NSString *scheme([[url scheme] lowercaseString]);
+ upgrades_ = 0;
+ bool unseens = false;
- NSString *path;
+ CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle));
- if ([scheme isEqualToString:@"cydia"])
- path = [href substringFromIndex:8];
- else if ([scheme isEqualToString:@"about"])
- path = [href substringFromIndex:12];
- else _assert(false);
+ for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
+ Package *package = [packages_ objectAtIndex:offset];
- NSRange slash([path rangeOfString:@"/"]);
+ BOOL uae = [package upgradableAndEssential:YES];
- NSString *command;
- if (slash.location == NSNotFound) {
- command = path;
- path = nil;
- } else {
- command = [path substringToIndex:slash.location];
- path = [path substringFromIndex:(slash.location + 1)];
+ if (!uae) {
+ unseens = true;
+ time_t seen([package seen]);
+
+ if (section == nil || last != seen) {
+ last = seen;
+
+ NSString *name;
+ name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) [NSDate dateWithTimeIntervalSince1970:seen]);
+ [name autorelease];
+
+ _profile(ChangesPackageListController$reloadData$Allocate)
+ name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name];
+ section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease];
+ [sections_ addObject:section];
+ _end
+ }
+
+ [section addToCount];
+ } else if ([package ignored]) {
+ if (ignored == nil) {
+ ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") row:offset localize:NO] autorelease];
+ }
+ [ignored addToCount];
+ } else {
+ ++upgrades_;
+ [upgradable addToCount];
+ }
}
+ _trace();
- Database *database([Database sharedInstance]);
+ CFRelease(formatter);
- if ([command isEqualToString:@"package-icon"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- Package *package([database packageWithName:path]);
- if (package == nil)
- goto fail;
- [package parse];
- UIImage *icon([package icon]);
- [self _returnPNGWithImage:icon forRequest:request];
- } else if ([command isEqualToString:@"uikit-image"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- UIImage *icon(_UIImageWithName(path));
- [self _returnPNGWithImage:icon forRequest:request];
- } else if ([command isEqualToString:@"section-icon"]) {
- if (path == nil)
- goto fail;
- path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- UIImage *icon([UIImage imageAtPath:[NSString stringWithFormat:@"%@/Sections/%@.png", App_, [path stringByReplacingOccurrencesOfString:@" " withString:@"_"]]]);
- if (icon == nil)
- icon = [UIImage applicationImageNamed:@"unknown.png"];
- [self _returnPNGWithImage:icon forRequest:request];
- } else fail: {
- [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorResourceUnavailable userInfo:nil]];
+ if (unseens) {
+ Section *last = [sections_ lastObject];
+ size_t count = [last count];
+ [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
+ [sections_ removeLastObject];
}
+
+ if ([ignored count] != 0)
+ [sections_ insertObject:ignored atIndex:0];
+ if (upgrades_ != 0)
+ [sections_ insertObject:upgradable atIndex:0];
+} }
+
+- (int) upgrades {
+ return upgrades_;
}
-- (void) stopLoading {
++ (BOOL) supportsSearch {
+ return NO;
}
@end
-/* }}} */
-/* Section Controller {{{ */
-@interface SectionController : FilteredPackageListController {
+@interface ChangesPackageListController : FilteredPackageListController <
+ CyteWebViewDelegate
+> {
+ _H<CyteWebView, 1> dickbar_;
_H<IndirectDelegate, 1> indirect_;
_H<CydiaObject> cydia_;
- _H<NSString> section_;
- _H<NSString> key_;
- _transient Source *source_;
- std::vector< _H<CyteWebViewTableViewCell, 1> > promoted_;
}
-- (id) initWithDatabase:(Database *)database section:(NSString *)section source:(Source *)source;
+- (id) initWithDatabase:(Database *)database;
@end
-@implementation SectionController
-
-- (NSURL *) referrerURL {
- NSMutableString *path = [NSMutableString string];
-
- if (source_ != nil) {
- [path appendString:[NSString stringWithFormat:@"sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
- } else {
- [path appendString:@"sections"];
- }
-
- [path appendString:@"/"];
+@implementation ChangesPackageListController
- if (section_ != nil) {
- [path appendString:[NSString stringWithFormat:@"%@", [section_ stringByAddingPercentEscapesIncludingReserved]]];
- } else {
- [path appendString:@"all"];
- }
+- (NSURL *) navigationURL {
+ return [NSURL URLWithString:@"cydia://changes"];
+}
- return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/%@", UI_, path]];
+- (void) viewDidAppear:(BOOL)animated {
+ [super viewDidAppear:animated];
+ [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
}
-- (NSURL *) navigationURL {
- NSMutableString *path = [NSMutableString string];
+- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
+ Package *package([datasource_ packageAtIndexPath:path]);
+ CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[NSString stringWithFormat:@"%@/#!/changes/", UI_]] autorelease]);
+ [view setDelegate:delegate_];
+ [[self navigationController] pushViewController:view animated:YES];
+}
- if (source_ != nil) {
- [path appendString:[NSString stringWithFormat:@"sources/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
- } else {
- [path appendString:@"sections"];
- }
++ (Class) dataSourceClass {
+ return [ChangesPackageListDataSource class];
+}
- [path appendString:@"/"];
+- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+ NSString *context([alert context]);
- if (section_ != nil) {
- [path appendString:[NSString stringWithFormat:@"%@", [section_ stringByAddingPercentEscapesIncludingReserved]]];
+ if ([context isEqualToString:@"norefresh"])
+ [alert dismissWithClickedButtonIndex:-1 animated:YES];
+}
+
+- (void) refreshButtonClicked {
+ if (IsReachable("cydia.saurik.com")) {
+ [delegate_ beginUpdate];
+ [[self navigationItem] setLeftBarButtonItem:nil animated:YES];
} else {
- [path appendString:@"all"];
+ UIAlertView *alert = [[[UIAlertView alloc]
+ initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("REFRESH")]
+ message:@"Host Unreachable" // XXX: Localize
+ delegate:self
+ cancelButtonTitle:UCLocalize("OK")
+ otherButtonTitles:nil
+ ] autorelease];
+
+ [alert setContext:@"norefresh"];
+ [alert show];
}
+}
- return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://%@", path]];
+- (void) upgradeButtonClicked {
+ [delegate_ distUpgrade];
+ [[self navigationItem] setRightBarButtonItem:nil animated:YES];
}
-- (id) initWithDatabase:(Database *)database section:(NSString *)name source:(Source *)source {
- NSString *title;
- if (name == nil)
- title = UCLocalize("ALL_PACKAGES");
- else if (![name isEqual:@""])
- title = [[NSBundle mainBundle] localizedStringForKey:Simplify(name) value:nil table:@"Sections"];
- else
- title = UCLocalize("NO_SECTION");
+- (void) loadView {
+ [super loadView];
- if ((self = [super initWithDatabase:database title:title]) != nil) {
- indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
- cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease];
+ if (AprilFools_ && kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
+ CGRect dickframe([[self view] bounds]);
+ dickframe.size.height = 44;
- [datasource_ addFilter:@"section" withSelector:@selector(isVisibleInSection:) priority:kPackageListFilterPriorityHigh object:name];
- [datasource_ addFilter:@"source" withSelector:@selector(isVisibleInSource:) priority:kPackageListFilterPriorityHigh object:source];
+ dickbar_ = [[[CyteWebView alloc] initWithFrame:dickframe] autorelease];
+ [dickbar_ setDelegate:self];
+ [[self view] addSubview:dickbar_];
- section_ = name;
- source_ = source;
- key_ = [source key];
- } return self;
-}
+ [dickbar_ setBackgroundColor:[UIColor clearColor]];
+ [dickbar_ setScalesPageToFit:YES];
-/*- (NSInteger) numberOfSectionsInTableView:(UITableView *)list {
- return [super numberOfSectionsInTableView:list] + 1;
-}
+ UIWebDocumentView *document([dickbar_ _documentView]);
+ [document setBackgroundColor:[UIColor clearColor]];
+ [document setDrawsBackground:NO];
-- (NSString *) tableView:(UITableView *)list titleForHeaderInSection:(NSInteger)section {
- return section == 0 ? nil : [super tableView:list titleForHeaderInSection:(section - 1)];
-}
+ WebView *webview([document webView]);
+ [webview setShouldUpdateWhileOffscreen:NO];
-- (NSInteger) tableView:(UITableView *)list numberOfRowsInSection:(NSInteger)section {
- return section == 0 ? promoted_.size() : [super tableView:list numberOfRowsInSection:(section - 1)];
-}
+ UIScrollView *scroller([dickbar_ scrollView]);
+ [scroller setScrollingEnabled:NO];
+ [scroller setFixedBackgroundPattern:YES];
+ [scroller setBackgroundColor:[UIColor clearColor]];
-+ (NSIndexPath *) adjustedIndexPath:(NSIndexPath *)path {
- return [NSIndexPath indexPathForRow:[path row] inSection:([path section] - 1)];
-}
+ WebPreferences *preferences([webview preferences]);
+ [preferences setCacheModel:WebCacheModelDocumentBrowser];
+ [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
+ [preferences setOfflineWebApplicationCacheEnabled:YES];
-- (UITableViewCell *) tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)path {
- if ([path section] != 0)
- return [super tableView:table cellForRowAtIndexPath:[SectionController adjustedIndexPath:path]];
+ [dickbar_ loadRequest:[NSURLRequest
+ requestWithURL:[Diversion divertURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/dickbar/", UI_]]]
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:120
+ ]];
- return promoted_[[path row]];
-}
+ UIEdgeInsets inset = {44, 0, 0, 0};
+ [list_ setContentInset:inset];
-- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
- if ([path section] != 0)
- return [super tableView:table didSelectRowAtIndexPath:[SectionController adjustedIndexPath:path]];
+ [dickbar_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
+ }
}
-- (NSInteger) tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
- NSInteger section([super tableView:tableView sectionForSectionIndexTitle:title atIndex:index]);
- return section == 0 ? 0 : section + 1;
-}*/
-
- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
NSURL *url([request URL]);
if (url == nil)
[CydiaWebViewController didClearWindowObject:window forFrame:frame withCydia:cydia_];
}
-- (void) loadView {
- [super loadView];
-
- // XXX: this code is horrible. I mean, wtf Jay?
- if (ShowPromoted_ && [[Metadata_ objectForKey:@"ShowPromoted"] boolValue]) {
- promoted_.resize(1);
-
- for (unsigned i(0); i != promoted_.size(); ++i) {
- CyteWebViewTableViewCell *promoted([CyteWebViewTableViewCell cellWithRequest:[NSURLRequest
- requestWithURL:[Diversion divertURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/sectionhead/%u/%@",
- UI_, i, section_ == nil ? @"" : [section_ stringByAddingPercentEscapesIncludingReserved]]
- ]]
-
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:120
- ]]);
-
- [promoted setDelegate:self];
- promoted_[i] = promoted;
- }
- }
-}
-
- (void) setDelegate:(id)delegate {
[super setDelegate:delegate];
[cydia_ setDelegate:delegate];
}
- (void) releaseSubviews {
- promoted_.clear();
+ dickbar_ = nil;
+
[super releaseSubviews];
}
-- (void)reloadData {
- source_ = [database_ sourceWithKey:key_];
- key_ = [source_ key];
- [datasource_ setObject:source_ forFilter:@"source"];
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super initWithDatabase:database title:(AprilFools_ ? @"Timeline" : UCLocalize("CHANGES"))]) != nil) {
+ indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
+ cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease];
+ database_ = database;
+ } return self;
+}
+- (void) reloadData {
[super reloadData];
+
+ [[self navigationItem] setRightBarButtonItem:([datasource_ upgrades] == 0 ? nil : [[[UIBarButtonItem alloc]
+ initWithTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", [datasource_ upgrades]]]
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(upgradeButtonClicked)
+ ] autorelease]) animated:YES];
+
+ [[self navigationItem] setLeftBarButtonItem:([delegate_ updating] ? nil : [[[UIBarButtonItem alloc]
+ initWithTitle:UCLocalize("REFRESH")
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(refreshButtonClicked)
+ ] autorelease]) animated:YES];
+
+ PrintTimes();
}
@end
/* }}} */
-/* Sections Controller {{{ */
-@interface SectionsController : CyteViewController <
+/* Package Settings Controller {{{ */
+@interface PackageSettingsController : CyteViewController <
UITableViewDataSource,
UITableViewDelegate
> {
_transient Database *database_;
- _H<NSMutableArray> sections_;
- _H<NSMutableArray> filtered_;
- _H<UITableView, 2> list_;
- _H<NSString> key_;
- _transient Source *source_;
+ _H<NSString> name_;
+ _H<Package> package_;
+ _H<UITableView, 2> table_;
+ _H<UISwitch> subscribedSwitch_;
+ _H<UISwitch> ignoredSwitch_;
+ _H<UITableViewCell> subscribedCell_;
+ _H<UITableViewCell> ignoredCell_;
}
-- (id) initWithDatabase:(Database *)database source:(Source *)source;
-- (void) editButtonClicked;
+- (id) initWithDatabase:(Database *)database package:(NSString *)package;
@end
-@implementation SectionsController
+@implementation PackageSettingsController
- (NSURL *) navigationURL {
- if (source_ != nil) {
- return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://sections/%@", [key_ stringByAddingPercentEscapesIncludingReserved]]];
- } else {
- return [NSURL URLWithString:@"cydia://sections"];
- }
+ return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/settings", (id) name_]];
}
-- (NSString *)defaultTitle {
- if (source_ != nil) {
- return [source_ label];
- } else {
- return UCLocalize("SECTIONS");
- }
+- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
+ if (package_ == nil)
+ return 0;
+
+ if ([package_ installed] == nil)
+ return 1;
+ else
+ return 2;
}
-- (void) updateNavigationItem {
- [[self navigationItem] setTitle:[self isEditing] ? UCLocalize("SECTION_VISIBILITY") : [self defaultTitle]];
- if ([sections_ count] == 0) {
- [[self navigationItem] setRightBarButtonItem:nil];
- } else {
- [[self navigationItem] setRightBarButtonItem:[[UIBarButtonItem alloc]
- initWithBarButtonSystemItem:([self isEditing] ? UIBarButtonSystemItemDone : UIBarButtonSystemItemEdit)
- target:self
- action:@selector(editButtonClicked)
- ] animated:([[self navigationItem] rightBarButtonItem] != nil)];
- }
+- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+ if (package_ == nil)
+ return 0;
+
+ // both sections contain just one item right now.
+ return 1;
}
-- (void) setEditing:(BOOL)editing animated:(BOOL)animated {
- [super setEditing:editing animated:animated];
+- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
+ return nil;
+}
- if (editing)
- [list_ reloadData];
+- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
+ if (section == 0)
+ return UCLocalize("SHOW_ALL_CHANGES_EX");
else
+ return UCLocalize("IGNORE_UPGRADES_EX");
+}
+
+- (void) onSubscribed:(id)control {
+ bool value([control isOn]);
+ if (package_ == nil)
+ return;
+ if ([package_ setSubscribed:value])
[delegate_ updateData];
-
- [self updateNavigationItem];
}
-- (void) viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
-}
+- (void) _updateIgnored {
+ const char *package([name_ UTF8String]);
+ bool on([ignoredSwitch_ isOn]);
-- (void) viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
- [self setEditing:NO];
-}
+ pid_t pid(ExecFork());
+ if (pid == 0) {
+ FILE *dpkg(popen("dpkg --set-selections", "w"));
+ fwrite(package, strlen(package), 1, dpkg);
-- (Section *) sectionAtIndexPath:(NSIndexPath *)indexPath {
- Section *section = nil;
- int index = [indexPath row];
- if (![self isEditing]) {
- index -= 1;
- if (index >= 0)
- section = [filtered_ objectAtIndex:index];
- } else {
- section = [sections_ objectAtIndex:index];
+ if (on)
+ fwrite(" hold\n", 6, 1, dpkg);
+ else
+ fwrite(" install\n", 9, 1, dpkg);
+
+ pclose(dpkg);
+
+ exit(0);
+ _assert(false);
}
- return section;
-}
-- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- if ([self isEditing])
- return [sections_ count];
- else
- return [filtered_ count] + 1;
+ ReapZombie(pid);
}
-/*- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- return 45.0f;
-}*/
+- (void) onIgnored:(id)control {
+ NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(_updateIgnored)]]);
+ [invocation setTarget:self];
+ [invocation setSelector:@selector(_updateIgnored)];
+
+ [delegate_ reloadDataWithInvocation:invocation];
+}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- static NSString *reuseIdentifier = @"SectionCell";
+ if (package_ == nil)
+ return nil;
- SectionCell *cell = (SectionCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
- if (cell == nil)
- cell = [[[SectionCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
+ switch ([indexPath section]) {
+ case 0: return subscribedCell_;
+ case 1: return ignoredCell_;
- [cell setSection:[self sectionAtIndexPath:indexPath] editing:[self isEditing]];
+ _nodefault
+ }
- return cell;
+ return nil;
}
-- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- if ([self isEditing])
- return;
+- (void) loadView {
+ UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]);
+ [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
+ [self setView:view];
- Section *section = [self sectionAtIndexPath:indexPath];
+ table_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease];
+ [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
+ [(UITableView *) table_ setDataSource:self];
+ [table_ setDelegate:self];
+ [view addSubview:table_];
- SectionController *controller = [[[SectionController alloc]
- initWithDatabase:database_
- section:[section name]
- source:source_
- ] autorelease];
- [controller setDelegate:delegate_];
+ subscribedSwitch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)] autorelease];
+ [subscribedSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+ [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventValueChanged];
- [[self navigationController] pushViewController:controller animated:YES];
-}
+ ignoredSwitch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)] autorelease];
+ [ignoredSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
+ [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventValueChanged];
-- (void) loadView {
- list_ = [[[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease];
- [list_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [list_ setRowHeight:45.0f];
- [(UITableView *) list_ setDataSource:self];
- [list_ setDelegate:self];
- [self setView:list_];
+ subscribedCell_ = [[[UITableViewCell alloc] init] autorelease];
+ [subscribedCell_ setText:UCLocalize("SHOW_ALL_CHANGES")];
+ [subscribedCell_ setAccessoryView:subscribedSwitch_];
+ [subscribedCell_ setSelectionStyle:UITableViewCellSelectionStyleNone];
+
+ ignoredCell_ = [[[UITableViewCell alloc] init] autorelease];
+ [ignoredCell_ setText:UCLocalize("IGNORE_UPGRADES")];
+ [ignoredCell_ setAccessoryView:ignoredSwitch_];
+ [ignoredCell_ setSelectionStyle:UITableViewCellSelectionStyleNone];
}
- (void) viewDidLoad {
[super viewDidLoad];
- [[self navigationItem] setTitle:[self defaultTitle]];
+ [[self navigationItem] setTitle:UCLocalize("SETTINGS")];
}
- (void) releaseSubviews {
- list_ = nil;
-
- sections_ = nil;
- filtered_ = nil;
+ ignoredCell_ = nil;
+ subscribedCell_ = nil;
+ table_ = nil;
+ ignoredSwitch_ = nil;
+ subscribedSwitch_ = nil;
[super releaseSubviews];
}
-- (id) initWithDatabase:(Database *)database source:(Source *)source {
+- (id) initWithDatabase:(Database *)database package:(NSString *)package {
if ((self = [super init]) != nil) {
database_ = database;
- source_ = source;
- key_ = [source_ key];
+ name_ = package;
} return self;
}
- (void) reloadData {
- source_ = [database_ sourceWithKey:key_];
- key_ = [source_ key];
-
- [[self navigationItem] setTitle:[source_ label]];
-
[super reloadData];
- NSArray *packages = [database_ packages];
+ package_ = [database_ packageWithName:name_];
- sections_ = [NSMutableArray arrayWithCapacity:16];
- filtered_ = [NSMutableArray arrayWithCapacity:16];
+ if (package_ != nil) {
+ [subscribedSwitch_ setOn:([package_ subscribed] ? 1 : 0) animated:NO];
+ [ignoredSwitch_ setOn:([package_ ignored] ? 1 : 0) animated:NO];
+ } // XXX: what now, G?
- NSMutableDictionary *sections([NSMutableDictionary dictionaryWithCapacity:32]);
+ [table_ reloadData];
+}
- _trace();
- for (Package *package in packages) {
- // Ignore packages from other sources (but allow all without a source).
- if (source_ != nil && ![package isVisibleInSource:source_]) continue;
+@end
+/* }}} */
- NSString *name([package section]);
- NSString *key(name == nil ? @"" : name);
+/* Installed Controller {{{ */
+@interface InstalledController : FilteredPackageListController {
+ BOOL expert_;
+}
- Section *section;
+- (id) initWithDatabase:(Database *)database;
- _profile(SectionsView$reloadData$Section)
- section = [sections objectForKey:key];
- if (section == nil) {
- _profile(SectionsView$reloadData$Section$Allocate)
- section = [[[Section alloc] initWithName:key localize:YES] autorelease];
- [sections setObject:section forKey:key];
- _end
- }
- _end
+- (void) updateRoleButton;
- [section addToCount];
+@end
- _profile(SectionsView$reloadData$Filter)
- if (![package valid] || ![package visible])
- continue;
- _end
+@implementation InstalledController
- [section addToRow];
- }
- _trace();
+- (NSURL *) referrerURL {
+ return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/installed/", UI_]];
+}
- [sections_ addObjectsFromArray:[sections allValues]];
+- (NSURL *) navigationURL {
+ return [NSURL URLWithString:@"cydia://installed"];
+}
- [sections_ sortUsingSelector:@selector(compareByLocalized:)];
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") ]) != nil) {
+ [datasource_ addFilter:@"installed" withSelector:@selector(isInstalledAndUnfiltered:) priority:kPackageListFilterPriorityHigh object:[NSNumber numberWithBool:YES]];
+ [self updateRoleButton];
+ } return self;
+}
- for (Section *section in (id) sections_) {
- size_t count([section row]);
- if (count == 0)
- continue;
+- (void) updateRoleButton {
+ if (Role_ != nil && ![Role_ isEqualToString:@"Developer"])
+ [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
+ initWithTitle:(expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE"))
+ style:(expert_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
+ target:self
+ action:@selector(roleButtonClicked)
+ ] autorelease]];
+}
- section = [[[Section alloc] initWithName:[section name] localized:[section localized]] autorelease];
- [section setCount:count];
- [filtered_ addObject:section];
- }
+- (void) roleButtonClicked {
+ [datasource_ setObject:[NSNumber numberWithBool:expert_] forFilter:@"installed"];
+ [self reloadData];
+ expert_ = !expert_;
- [self updateNavigationItem];
- [list_ reloadData];
- _trace();
+ [self updateRoleButton];
}
-- (void) editButtonClicked {
- [self setEditing:![self isEditing] animated:YES];
+@end
+/* }}} */
+
+/* Confirmation Controller {{{ */
+bool DepSubstrate(const pkgCache::VerIterator &iterator) {
+ if (!iterator.end())
+ for (pkgCache::DepIterator dep(iterator.DependsList()); !dep.end(); ++dep) {
+ if (dep->Type != pkgCache::Dep::Depends && dep->Type != pkgCache::Dep::PreDepends)
+ continue;
+ pkgCache::PkgIterator package(dep.TargetPkg());
+ if (package.end())
+ continue;
+ if (strcmp(package.Name(), "mobilesubstrate") == 0)
+ return true;
+ }
+
+ return false;
}
+@protocol ConfirmationControllerDelegate
+- (void) clearQueue;
+- (void) confirmWithNavigationController:(UINavigationController *)navigation;
@end
-/* }}} */
-/* Changes Controller {{{ */
-@interface ChangesPackageListDataSource : FilteredPackageListDataSource {
- unsigned upgrades_;
+@interface ConfirmationControllerDataSource : FilteredPackageListDataSource {
+ _H<NSMutableArray> issues_;
+ BOOL removeEssential_;
+ BOOL substrate_;
}
+@property (nonatomic, readonly) NSMutableArray *issues;
+@property (nonatomic, readonly, assign, getter=willRemoveEssential) BOOL removeEssential;
+@property (nonatomic, readonly, assign) BOOL substrate;
+
@end
-@implementation ChangesPackageListDataSource
+@implementation ConfirmationControllerDataSource
+
+- (int) numberOfSectionsInTableView:(UITableView *)table {
+int n = [super numberOfSectionsInTableView:table];
+ NSLog(@"nubmer of sectoins: %d");
+ return n;
+}
+
+- (NSMutableArray *)issues {
+ return issues_;
+}
+
+- (BOOL)willRemoveEssential {
+ return removeEssential_;
+}
+
+- (BOOL)substrate {
+ return substrate_;
+}
+
++ (BOOL)supportsSearch {
+ return NO;
+}
+
+- (void)reloadData {
+ bool remove(false);
-- (NSMutableArray *) _reloadPackages {
-@synchronized (database_) {
era_ = [database_ era];
+
+ pkgCacheFile &cache([database_ cache]);
NSArray *packages([database_ packages]);
+ pkgDepCache::Policy *policy([database_ policy]);
- NSMutableArray *filtered([NSMutableArray arrayWithCapacity:[packages count]]);
+ issues_ = [NSMutableArray arrayWithCapacity:4];
- _trace();
- _profile(ChangesPackageListController$_reloadPackages$Filter)
- for (Package *package in packages)
- if ([package upgradableAndEssential:YES] || [package visible])
- CFArrayAppendValue((CFMutableArrayRef) filtered, package);
- _end
- _trace();
- _profile(ChangesPackageListController$_reloadPackages$radixSort)
- [filtered radixSortUsingFunction:reinterpret_cast<MenesRadixSortFunction>(&PackageChangesRadix) withContext:NULL];
- _end
- _trace();
+ Section *installsSection = [[[Section alloc] initWithName:@"Install" localized:UCLocalize("INSTALLS")] autorelease];
+ Section *reinstallsSection = [[[Section alloc] initWithName:@"Reinstall" localized:UCLocalize("REINSTALLS")] autorelease];
+ Section *upgradesSection = [[[Section alloc] initWithName:@"Upgrade" localized:UCLocalize("UPGRADES")] autorelease];
+ Section *downgradesSection = [[[Section alloc] initWithName:@"Downgrade" localized:UCLocalize("DOWNGRADES")] autorelease];
+ Section *removesSection = [[[Section alloc] initWithName:@"Remove" localized:UCLocalize("REMOVES")] autorelease];
- return filtered;
-} }
+ NSMutableArray *installs = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *reinstalls = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *upgrades = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *downgrades = [NSMutableArray arrayWithCapacity:16];
+ NSMutableArray *removes = [NSMutableArray arrayWithCapacity:16];
-- (void) reloadData {
- NSArray *packages = [self _reloadPackages];
+ for (Package *package in packages) {
+ pkgCache::PkgIterator iterator([package iterator]);
+ NSString *name([package id]);
-@synchronized (database_) {
- packages_ = packages;
- sections_ = [NSMutableArray arrayWithCapacity:16];
+ if ([package broken]) {
+ NSMutableArray *reasons([NSMutableArray arrayWithCapacity:4]);
- Section *upgradable = [[[Section alloc] initWithName:UCLocalize("AVAILABLE_UPGRADES") localize:NO] autorelease];
- Section *ignored = nil;
- Section *section = nil;
- time_t last = 0;
+ [issues_ addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ name, @"package",
+ reasons, @"reasons",
+ nil]];
- upgrades_ = 0;
- bool unseens = false;
+ pkgCache::VerIterator ver(cache[iterator].InstVerIter(cache));
+ if (ver.end())
+ continue;
- CFDateFormatterRef formatter(CFDateFormatterCreate(NULL, Locale_, kCFDateFormatterMediumStyle, kCFDateFormatterMediumStyle));
+ for (pkgCache::DepIterator dep(ver.DependsList()); !dep.end(); ) {
+ pkgCache::DepIterator start;
+ pkgCache::DepIterator end;
+ dep.GlobOr(start, end); // ++dep
- for (size_t offset = 0, count = [packages_ count]; offset != count; ++offset) {
- Package *package = [packages_ objectAtIndex:offset];
+ if (!cache->IsImportantDep(end))
+ continue;
+ if ((cache[end] & pkgDepCache::DepGInstall) != 0)
+ continue;
- BOOL uae = [package upgradableAndEssential:YES];
+ NSMutableArray *clauses([NSMutableArray arrayWithCapacity:4]);
- if (!uae) {
- unseens = true;
- time_t seen([package seen]);
+ [reasons addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithUTF8String:start.DepType()], @"relationship",
+ clauses, @"clauses",
+ nil]];
- if (section == nil || last != seen) {
- last = seen;
+ _forever {
+ NSString *reason, *installed((NSString *) [WebUndefined undefined]);
+
+ pkgCache::PkgIterator target(start.TargetPkg());
+ if (target->ProvidesList != 0)
+ reason = @"missing";
+ else {
+ pkgCache::VerIterator ver(cache[target].InstVerIter(cache));
+ if (!ver.end()) {
+ reason = @"installed";
+ installed = [NSString stringWithUTF8String:ver.VerStr()];
+ } else if (!cache[target].CandidateVerIter(cache).end())
+ reason = @"uninstalled";
+ else if (target->ProvidesList == 0)
+ reason = @"uninstallable";
+ else
+ reason = @"virtual";
+ }
- NSString *name;
- name = (NSString *) CFDateFormatterCreateStringWithDate(NULL, formatter, (CFDateRef) [NSDate dateWithTimeIntervalSince1970:seen]);
- [name autorelease];
+ NSDictionary *version(start.TargetVer() == 0 ? [NSNull null] : [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithUTF8String:start.CompType()], @"operator",
+ [NSString stringWithUTF8String:start.TargetVer()], @"value",
+ nil]);
+ [clauses addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithUTF8String:start.TargetPkg().Name()], @"package",
+ version, @"version",
+ reason, @"reason",
+ installed, @"installed",
+ nil]];
- _profile(ChangesPackageListController$reloadData$Allocate)
- name = [NSString stringWithFormat:UCLocalize("NEW_AT"), name];
- section = [[[Section alloc] initWithName:name row:offset localize:NO] autorelease];
- [sections_ addObject:section];
- _end
+ // yes, seriously. (wtf?)
+ if (start == end)
+ break;
+ ++start;
+ }
}
+ }
- [section addToCount];
- } else if ([package ignored]) {
- if (ignored == nil) {
- ignored = [[[Section alloc] initWithName:UCLocalize("IGNORED_UPGRADES") row:offset localize:NO] autorelease];
- }
- [ignored addToCount];
+ pkgDepCache::StateCache &state(cache[iterator]);
+
+ static Pcre special_r("^(firmware$|gsc\\.|cy\\+)");
+
+ if (state.NewInstall()) {
+ [installs addObject:package];
+ [installsSection addToCount];
+ // XXX: } else if (state.Install()) {
+ } else if (!state.Delete() && (state.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall) {
+ [reinstalls addObject:package];
+ [reinstallsSection addToCount];
+ // XXX: move before previous if
+ } else if (state.Upgrade()) {
+ [upgrades addObject:package];
+ [upgradesSection addToCount];
+ } else if (state.Downgrade()) {
+ [downgrades addObject:package];
+ [downgradesSection addToCount];
+ } else if (!state.Delete()) {
+ // XXX: _assert(state.Keep());
+ continue;
+ } else if (special_r(name)) {
+ [issues_ addObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNull null], @"package",
+ [NSArray arrayWithObjects:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ @"Conflicts", @"relationship",
+ [NSArray arrayWithObjects:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ name, @"package",
+ [NSNull null], @"version",
+ @"installed", @"reason",
+ nil],
+ nil], @"clauses",
+ nil],
+ nil], @"reasons",
+ nil]];
} else {
- ++upgrades_;
- [upgradable addToCount];
+ if ([package essential])
+ remove = true;
+ [removes addObject:package];
+ [removesSection addToCount];
}
- }
- _trace();
-
- CFRelease(formatter);
-
- if (unseens) {
- Section *last = [sections_ lastObject];
- size_t count = [last count];
- [packages_ removeObjectsInRange:NSMakeRange([packages_ count] - count, count)];
- [sections_ removeLastObject];
- }
-
- if ([ignored count] != 0)
- [sections_ insertObject:ignored atIndex:0];
- if (upgrades_ != 0)
- [sections_ insertObject:upgradable atIndex:0];
-} }
-
-- (int) upgrades {
- return upgrades_;
-}
-+ (BOOL) supportsSearch {
- return NO;
+ substrate_ |= DepSubstrate(policy->GetCandidateVer(iterator));
+ substrate_ |= DepSubstrate(iterator.CurrentVer());
+ }
+
+ /*sizes_ = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInteger:[database_ fetcher].FetchNeeded()], @"downloading",
+ [NSNumber numberWithInteger:[database_ fetcher].PartialPresent()], @"resuming",
+ nil];*/
+
+ NSMutableArray *packagesList = [NSMutableArray arrayWithCapacity:64];
+ size_t count = 0;
+ count += [installs count];
+ [packagesList addObjectsFromArray:installs];
+ for (size_t i = 0; i < count; i++) [reinstallsSection addToRow];
+ count += [reinstalls count];
+ [packagesList addObjectsFromArray:reinstalls];
+ for (size_t i = 0; i < count; i++) [upgradesSection addToRow];
+ count += [upgrades count];
+ [packagesList addObjectsFromArray:upgrades];
+ for (size_t i = 0; i < count; i++) [downgradesSection addToRow];
+ count += [downgrades count];
+ [packagesList addObjectsFromArray:downgrades];
+ for (size_t i = 0; i < count; i++) [removesSection addToRow];
+ [packagesList addObjectsFromArray:removes];
+ packages_ = packagesList;
+
+ sections_ = [NSMutableArray arrayWithCapacity:4];
+ if ([installsSection count] > 0) [sections_ addObject:installsSection];
+ if ([reinstallsSection count] > 0) [sections_ addObject:reinstallsSection];
+ if ([upgradesSection count] > 0) [sections_ addObject:upgradesSection];
+ if ([downgradesSection count] > 0) [sections_ addObject:downgradesSection];
+ if ([removesSection count] > 0) [sections_ addObject:removesSection];
}
@end
-@interface ChangesPackageListController : FilteredPackageListController <
- CyteWebViewDelegate
-> {
- _H<CyteWebView, 1> dickbar_;
- _H<IndirectDelegate, 1> indirect_;
- _H<CydiaObject> cydia_;
+@interface ConfirmationController : FilteredPackageListController {
+ _H<UIAlertView> essential_;
}
- (id) initWithDatabase:(Database *)database;
@end
-@implementation ChangesPackageListController
+@implementation ConfirmationController
-- (NSURL *) navigationURL {
- return [NSURL URLWithString:@"cydia://changes"];
++ (Class) dataSourceClass {
+ return [ConfirmationControllerDataSource class];
}
-- (void) viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
- [list_ deselectRowAtIndexPath:[list_ indexPathForSelectedRow] animated:animated];
+- (void) complete {
+ if ([datasource_ substrate])
+ RestartSubstrate_ = true;
+ [delegate_ confirmWithNavigationController:[self navigationController]];
}
-- (void) tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)path {
- Package *package([datasource_ packageAtIndexPath:path]);
- CYPackageController *view([[[CYPackageController alloc] initWithDatabase:database_ forPackage:[package id] withReferrer:[NSString stringWithFormat:@"%@/#!/changes/", UI_]] autorelease]);
- [view setDelegate:delegate_];
- [[self navigationController] pushViewController:view animated:YES];
+- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
+ NSString *context([alert context]);
+
+ if ([context isEqualToString:@"remove"]) {
+ if (button == [alert cancelButtonIndex])
+ [self dismissModalViewControllerAnimated:YES];
+ else if (button == [alert firstOtherButtonIndex]) {
+ [self performSelector:@selector(complete) withObject:nil afterDelay:0];
+ }
+
+ [alert dismissWithClickedButtonIndex:-1 animated:YES];
+ } else if ([context isEqualToString:@"unable"]) {
+ [self dismissModalViewControllerAnimated:YES];
+ [alert dismissWithClickedButtonIndex:-1 animated:YES];
+ }
}
-+ (Class) dataSourceClass {
- return [ChangesPackageListDataSource class];
+- (id) initWithDatabase:(Database *)database {
+ if ((self = [super initWithDatabase:database title:UCLocalize("QUEUE")]) != nil) {
+ } return self;
}
-- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)button {
- NSString *context([alert context]);
+- (void)viewWillAppear:(BOOL)animated {
+ [super viewWillAppear:animated];
- if ([context isEqualToString:@"norefresh"])
- [alert dismissWithClickedButtonIndex:-1 animated:YES];
+ [self reloadData];
}
-- (void) refreshButtonClicked {
- if (IsReachable("cydia.saurik.com")) {
- [delegate_ beginUpdate];
- [[self navigationItem] setLeftBarButtonItem:nil animated:YES];
+- (void)reloadData {
+ [super reloadData];
+
+ if (![datasource_ willRemoveEssential]) {
+ essential_ = nil;
+ } else if (Advanced_) {
+ NSString *parenthetical(UCLocalize("PARENTHETICAL"));
+
+ essential_ = [[[UIAlertView alloc]
+ initWithTitle:UCLocalize("REMOVING_ESSENTIALS")
+ message:UCLocalize("REMOVING_ESSENTIALS_EX")
+ delegate:self
+ cancelButtonTitle:[NSString stringWithFormat:parenthetical, UCLocalize("CANCEL_OPERATION"), UCLocalize("SAFE")]
+ otherButtonTitles:
+ [NSString stringWithFormat:parenthetical, UCLocalize("FORCE_REMOVAL"), UCLocalize("UNSAFE")],
+ nil
+ ] autorelease];
+
+ [essential_ setContext:@"remove"];
+ [essential_ setNumberOfRows:2];
} else {
- UIAlertView *alert = [[[UIAlertView alloc]
- initWithTitle:[NSString stringWithFormat:Colon_, Error_, UCLocalize("REFRESH")]
- message:@"Host Unreachable" // XXX: Localize
+ essential_ = [[[UIAlertView alloc]
+ initWithTitle:UCLocalize("UNABLE_TO_COMPLY")
+ message:UCLocalize("UNABLE_TO_COMPLY_EX")
delegate:self
- cancelButtonTitle:UCLocalize("OK")
+ cancelButtonTitle:UCLocalize("OKAY")
otherButtonTitles:nil
] autorelease];
- [alert setContext:@"norefresh"];
- [alert show];
+ [essential_ setContext:@"unable"];
+ }
+
+ if ([[datasource_ packages] count] > 0) {
+ [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
+ initWithTitle:UCLocalize("CLEAR")
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(clearButtonClicked)
+ ] autorelease]];
+ } else {
+ [[self navigationItem] setLeftBarButtonItem:nil];
+ }
+
+ if ([[datasource_ issues] count] == 0) {
+ [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
+ initWithTitle:UCLocalize("CONFIRM")
+ style:UIBarButtonItemStyleDone
+ target:self
+ action:@selector(confirmButtonClicked)
+ ] autorelease]];
+ } else {
+ [[self navigationItem] setRightBarButtonItem:nil];
}
}
-- (void) upgradeButtonClicked {
- [delegate_ distUpgrade];
- [[self navigationItem] setRightBarButtonItem:nil animated:YES];
+- (void) clearButtonClicked {
+ [delegate_ clearQueue];
+ [self reloadData];
}
-- (void) loadView {
- [super loadView];
+- (void) confirmButtonClicked {
+ if (essential_ != nil)
+ [essential_ show];
+ else
+ [self complete];
+}
- if (AprilFools_ && kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_0) {
- CGRect dickframe([[self view] bounds]);
- dickframe.size.height = 44;
+@end
+/* }}} */
- dickbar_ = [[[CyteWebView alloc] initWithFrame:dickframe] autorelease];
- [dickbar_ setDelegate:self];
- [[self view] addSubview:dickbar_];
+/* Progress Data {{{ */
+@interface CydiaProgressData : NSObject {
+ _transient id delegate_;
- [dickbar_ setBackgroundColor:[UIColor clearColor]];
- [dickbar_ setScalesPageToFit:YES];
+ bool running_;
+ float percent_;
- UIWebDocumentView *document([dickbar_ _documentView]);
- [document setBackgroundColor:[UIColor clearColor]];
- [document setDrawsBackground:NO];
+ float current_;
+ float total_;
+ float speed_;
- WebView *webview([document webView]);
- [webview setShouldUpdateWhileOffscreen:NO];
+ _H<NSMutableArray> events_;
+ _H<NSString> title_;
- UIScrollView *scroller([dickbar_ scrollView]);
- [scroller setScrollingEnabled:NO];
- [scroller setFixedBackgroundPattern:YES];
- [scroller setBackgroundColor:[UIColor clearColor]];
+ _H<NSString> status_;
+ _H<NSString> finish_;
+}
- WebPreferences *preferences([webview preferences]);
- [preferences setCacheModel:WebCacheModelDocumentBrowser];
- [preferences setJavaScriptCanOpenWindowsAutomatically:YES];
- [preferences setOfflineWebApplicationCacheEnabled:YES];
+@end
- [dickbar_ loadRequest:[NSURLRequest
- requestWithURL:[Diversion divertURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/dickbar/", UI_]]]
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:120
- ]];
+@implementation CydiaProgressData
+
++ (NSArray *) _attributeKeys {
+ return [NSArray arrayWithObjects:
+ @"current",
+ @"events",
+ @"finish",
+ @"percent",
+ @"running",
+ @"speed",
+ @"title",
+ @"total",
+ nil];
+}
+
+- (NSArray *) attributeKeys {
+ return [[self class] _attributeKeys];
+}
+
++ (BOOL) isKeyExcludedFromWebScript:(const char *)name {
+ return ![[self _attributeKeys] containsObject:[NSString stringWithUTF8String:name]] && [super isKeyExcludedFromWebScript:name];
+}
+
+- (id) init {
+ if ((self = [super init]) != nil) {
+ events_ = [NSMutableArray arrayWithCapacity:32];
+ } return self;
+}
- UIEdgeInsets inset = {44, 0, 0, 0};
- [list_ setContentInset:inset];
+- (void) setDelegate:(id)delegate {
+ delegate_ = delegate;
+}
- [dickbar_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
- }
+- (void) setPercent:(float)value {
+ percent_ = value;
}
-- (void) webView:(WebView *)view decidePolicyForNewWindowAction:(NSDictionary *)action request:(NSURLRequest *)request newFrameName:(NSString *)frame decisionListener:(id<WebPolicyDecisionListener>)listener {
- NSURL *url([request URL]);
- if (url == nil)
- return;
+- (NSNumber *) percent {
+ return [NSNumber numberWithFloat:percent_];
+}
- if ([frame isEqualToString:@"_open"])
- [delegate_ openURL:url];
- else {
- WebFrame *frame(nil);
- if (NSDictionary *WebActionElement = [action objectForKey:@"WebActionElementKey"])
- frame = [WebActionElement objectForKey:@"WebElementFrame"];
- if (frame == nil)
- frame = [view mainFrame];
+- (void) setCurrent:(float)value {
+ current_ = value;
+}
- WebDataSource *source([frame provisionalDataSource] ?: [frame dataSource]);
+- (NSNumber *) current {
+ return [NSNumber numberWithFloat:current_];
+}
- CyteViewController *controller([delegate_ pageForURL:url forExternal:NO withReferrer:([request valueForHTTPHeaderField:@"Referer"] ?: [[[source request] URL] absoluteString])] ?: [[[CydiaWebViewController alloc] initWithRequest:request] autorelease]);
- [controller setDelegate:delegate_];
- [[self navigationController] pushViewController:controller animated:YES];
- }
+- (void) setTotal:(float)value {
+ total_ = value;
+}
- [listener ignore];
+- (NSNumber *) total {
+ return [NSNumber numberWithFloat:total_];
}
-- (NSURLRequest *) webView:(WebView *)view resource:(id)resource willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)source {
- return [CydiaWebViewController requestWithHeaders:request];
+- (void) setSpeed:(float)value {
+ speed_ = value;
}
-- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
- [CydiaWebViewController didClearWindowObject:window forFrame:frame withCydia:cydia_];
+- (NSNumber *) speed {
+ return [NSNumber numberWithFloat:speed_];
}
-- (void) setDelegate:(id)delegate {
- [super setDelegate:delegate];
- [cydia_ setDelegate:delegate];
+- (NSArray *) events {
+ return events_;
}
-- (void) releaseSubviews {
- dickbar_ = nil;
+- (void) removeAllEvents {
+ [events_ removeAllObjects];
+}
- [super releaseSubviews];
+- (void) addEvent:(CydiaProgressEvent *)event {
+ [events_ addObject:event];
}
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super initWithDatabase:database title:(AprilFools_ ? @"Timeline" : UCLocalize("CHANGES"))]) != nil) {
- indirect_ = [[[IndirectDelegate alloc] initWithDelegate:self] autorelease];
- cydia_ = [[[CydiaObject alloc] initWithDelegate:indirect_] autorelease];
- database_ = database;
- } return self;
+- (void) setTitle:(NSString *)text {
+ title_ = text;
}
-- (void) reloadData {
- [super reloadData];
+- (NSString *) title {
+ return title_;
+}
- [[self navigationItem] setRightBarButtonItem:([datasource_ upgrades] == 0 ? nil : [[[UIBarButtonItem alloc]
- initWithTitle:[NSString stringWithFormat:UCLocalize("PARENTHETICAL"), UCLocalize("UPGRADE"), [NSString stringWithFormat:@"%u", [datasource_ upgrades]]]
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(upgradeButtonClicked)
- ] autorelease]) animated:YES];
+- (void) setFinish:(NSString *)text {
+ finish_ = text;
+}
- [[self navigationItem] setLeftBarButtonItem:([delegate_ updating] ? nil : [[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("REFRESH")
- style:UIBarButtonItemStylePlain
- target:self
- action:@selector(refreshButtonClicked)
- ] autorelease]) animated:YES];
+- (NSString *) finish {
+ return (id) finish_ ?: [NSNull null];
+}
- PrintTimes();
+- (void) setRunning:(bool)running {
+ running_ = running;
+}
+
+- (NSNumber *) running {
+ return running_ ? (NSNumber *) kCFBooleanTrue : (NSNumber *) kCFBooleanFalse;
}
@end
/* }}} */
-/* Package Settings Controller {{{ */
-@interface PackageSettingsController : CyteViewController <
- UITableViewDataSource,
- UITableViewDelegate
+/* Progress Controller {{{ */
+@interface ProgressController : CydiaWebViewController <
+ ProgressDelegate
> {
_transient Database *database_;
- _H<NSString> name_;
- _H<Package> package_;
- _H<UITableView, 2> table_;
- _H<UISwitch> subscribedSwitch_;
- _H<UISwitch> ignoredSwitch_;
- _H<UITableViewCell> subscribedCell_;
- _H<UITableViewCell> ignoredCell_;
+ _H<CydiaProgressData, 1> progress_;
+ unsigned cancel_;
}
-- (id) initWithDatabase:(Database *)database package:(NSString *)package;
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate;
+
+- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title;
+
+- (void) setTitle:(NSString *)title;
+- (void) setCancellable:(bool)cancellable;
@end
-@implementation PackageSettingsController
+@implementation ProgressController
-- (NSURL *) navigationURL {
- return [NSURL URLWithString:[NSString stringWithFormat:@"cydia://package/%@/settings", (id) name_]];
+- (void) dealloc {
+ [database_ setProgressDelegate:nil];
+ [super dealloc];
}
-- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
- if (package_ == nil)
- return 0;
+- (UIBarButtonItem *) leftButton {
+ return cancel_ == 1 ? [[[UIBarButtonItem alloc]
+ initWithTitle:UCLocalize("CANCEL")
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(cancel)
+ ] autorelease] : nil;
+}
- if ([package_ installed] == nil)
- return 1;
- else
- return 2;
+- (void) updateCancel {
+ [super applyLeftButton];
}
-- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- if (package_ == nil)
- return 0;
+- (id) initWithDatabase:(Database *)database delegate:(id)delegate {
+ if ((self = [super init]) != nil) {
+ database_ = database;
+ delegate_ = delegate;
- // both sections contain just one item right now.
- return 1;
-}
+ [database_ setProgressDelegate:self];
-- (NSString *) tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
- return nil;
+ progress_ = [[[CydiaProgressData alloc] init] autorelease];
+ [progress_ setDelegate:self];
+
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/progress/", UI_]]];
+
+ [scroller_ setBackgroundColor:[UIColor blackColor]];
+
+ [[self navigationItem] setHidesBackButton:YES];
+
+ [self updateCancel];
+ } return self;
}
-- (NSString *) tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section {
- if (section == 0)
- return UCLocalize("SHOW_ALL_CHANGES_EX");
- else
- return UCLocalize("IGNORE_UPGRADES_EX");
+- (void) webView:(WebView *)view didClearWindowObject:(WebScriptObject *)window forFrame:(WebFrame *)frame {
+ [super webView:view didClearWindowObject:window forFrame:frame];
+ [window setValue:progress_ forKey:@"cydiaProgress"];
}
-- (void) onSubscribed:(id)control {
- bool value([control isOn]);
- if (package_ == nil)
- return;
- if ([package_ setSubscribed:value])
- [delegate_ updateData];
+- (void) updateProgress {
+ [self dispatchEvent:@"CydiaProgressUpdate"];
}
-- (void) _updateIgnored {
- const char *package([name_ UTF8String]);
- bool on([ignoredSwitch_ isOn]);
+- (void) viewWillAppear:(BOOL)animated {
+ [[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlack];
+ [super viewWillAppear:animated];
+}
+- (void) reloadSpringBoard {
pid_t pid(ExecFork());
if (pid == 0) {
- FILE *dpkg(popen("dpkg --set-selections", "w"));
- fwrite(package, strlen(package), 1, dpkg);
+ pid_t pid(ExecFork());
+ if (pid == 0) {
+ execl("/usr/bin/sbreload", "sbreload", NULL);
+ perror("sbreload");
+ exit(0);
+ }
- if (on)
- fwrite(" hold\n", 6, 1, dpkg);
- else
- fwrite(" install\n", 9, 1, dpkg);
+ exit(0);
+ }
- pclose(dpkg);
+ ReapZombie(pid);
- exit(0);
- _assert(false);
+ sleep(15);
+ system("/usr/bin/killall SpringBoard");
+}
+
+- (void) close {
+ UpdateExternalStatus(0);
+
+ if (Finish_ > 1)
+ [delegate_ saveState];
+
+ switch (Finish_) {
+ case 0:
+ [delegate_ returnToCydia];
+ break;
+
+ case 1:
+ [delegate_ terminateWithSuccess];
+ /*if ([delegate_ respondsToSelector:@selector(suspendWithAnimation:)])
+ [delegate_ suspendWithAnimation:YES];
+ else
+ [delegate_ suspend];*/
+ break;
+
+ case 2:
+ _trace();
+ goto reload;
+
+ case 3:
+ _trace();
+ goto reload;
+
+ reload: {
+ UIProgressHUD *hud([delegate_ addProgressHUD]);
+ [hud setText:UCLocalize("LOADING")];
+ [self performSelector:@selector(reloadSpringBoard) withObject:nil afterDelay:0.5];
+ return;
+ }
+
+ case 4:
+ _trace();
+ if (void (*SBReboot)(mach_port_t) = reinterpret_cast<void (*)(mach_port_t)>(dlsym(RTLD_DEFAULT, "SBReboot")))
+ SBReboot(SBSSpringBoardServerPort());
+ else
+ reboot2(RB_AUTOBOOT);
+ break;
}
- ReapZombie(pid);
+ [super close];
}
-- (void) onIgnored:(id)control {
- NSInvocation *invocation([NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(_updateIgnored)]]);
- [invocation setTarget:self];
- [invocation setSelector:@selector(_updateIgnored)];
+- (void) setTitle:(NSString *)title {
+ [progress_ setTitle:title];
+ [self updateProgress];
+}
- [delegate_ reloadDataWithInvocation:invocation];
+- (UIBarButtonItem *) rightButton {
+ return [[progress_ running] boolValue] ? [super rightButton] : [[[UIBarButtonItem alloc]
+ initWithTitle:UCLocalize("CLOSE")
+ style:UIBarButtonItemStylePlain
+ target:self
+ action:@selector(close)
+ ] autorelease];
}
-- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- if (package_ == nil)
- return nil;
+- (void) invoke:(NSInvocation *)invocation withTitle:(NSString *)title {
+ UpdateExternalStatus(1);
- switch ([indexPath section]) {
- case 0: return subscribedCell_;
- case 1: return ignoredCell_;
+ [progress_ setRunning:true];
+ [self setTitle:title];
+ // implicit updateProgress
- _nodefault
+ SHA1SumValue notifyconf; {
+ FileFd file;
+ if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
+ _error->Discard();
+ else {
+ MMap mmap(file, MMap::ReadOnly);
+ SHA1Summation sha1;
+ sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+ notifyconf = sha1.Result();
+ }
}
- return nil;
-}
-
-- (void) loadView {
- UIView *view([[[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] autorelease]);
- [view setAutoresizingMask:(UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight)];
- [self setView:view];
+ SHA1SumValue springlist; {
+ FileFd file;
+ if (!file.Open(SpringBoard_, FileFd::ReadOnly))
+ _error->Discard();
+ else {
+ MMap mmap(file, MMap::ReadOnly);
+ SHA1Summation sha1;
+ sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+ springlist = sha1.Result();
+ }
+ }
- table_ = [[[UITableView alloc] initWithFrame:[[self view] bounds] style:UITableViewStyleGrouped] autorelease];
- [table_ setAutoresizingMask:UIViewAutoresizingFlexibleBoth];
- [(UITableView *) table_ setDataSource:self];
- [table_ setDelegate:self];
- [view addSubview:table_];
+ if (invocation != nil) {
+ [invocation yieldToSelector:@selector(invoke)];
+ [self setTitle:@"COMPLETE"];
+ }
- subscribedSwitch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)] autorelease];
- [subscribedSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
- [subscribedSwitch_ addTarget:self action:@selector(onSubscribed:) forEvents:UIControlEventValueChanged];
+ if (Finish_ < 4) {
+ FileFd file;
+ if (!file.Open(NotifyConfig_, FileFd::ReadOnly))
+ _error->Discard();
+ else {
+ MMap mmap(file, MMap::ReadOnly);
+ SHA1Summation sha1;
+ sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+ if (!(notifyconf == sha1.Result()))
+ Finish_ = 4;
+ }
+ }
- ignoredSwitch_ = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 50, 20)] autorelease];
- [ignoredSwitch_ setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin];
- [ignoredSwitch_ addTarget:self action:@selector(onIgnored:) forEvents:UIControlEventValueChanged];
+ if (Finish_ < 3) {
+ FileFd file;
+ if (!file.Open(SpringBoard_, FileFd::ReadOnly))
+ _error->Discard();
+ else {
+ MMap mmap(file, MMap::ReadOnly);
+ SHA1Summation sha1;
+ sha1.Add(reinterpret_cast<uint8_t *>(mmap.Data()), mmap.Size());
+ if (!(springlist == sha1.Result()))
+ Finish_ = 3;
+ }
+ }
- subscribedCell_ = [[[UITableViewCell alloc] init] autorelease];
- [subscribedCell_ setText:UCLocalize("SHOW_ALL_CHANGES")];
- [subscribedCell_ setAccessoryView:subscribedSwitch_];
- [subscribedCell_ setSelectionStyle:UITableViewCellSelectionStyleNone];
+ if (Finish_ < 2) {
+ if (RestartSubstrate_)
+ Finish_ = 2;
+ }
- ignoredCell_ = [[[UITableViewCell alloc] init] autorelease];
- [ignoredCell_ setText:UCLocalize("IGNORE_UPGRADES")];
- [ignoredCell_ setAccessoryView:ignoredSwitch_];
- [ignoredCell_ setSelectionStyle:UITableViewCellSelectionStyleNone];
-}
+ RestartSubstrate_ = false;
-- (void) viewDidLoad {
- [super viewDidLoad];
+ switch (Finish_) {
+ case 0: [progress_ setFinish:UCLocalize("RETURN_TO_CYDIA")]; break; /* XXX: Maybe UCLocalize("DONE")? */
+ case 1: [progress_ setFinish:UCLocalize("CLOSE_CYDIA")]; break;
+ case 2: [progress_ setFinish:UCLocalize("RESTART_SPRINGBOARD")]; break;
+ case 3: [progress_ setFinish:UCLocalize("RELOAD_SPRINGBOARD")]; break;
+ case 4: [progress_ setFinish:UCLocalize("REBOOT_DEVICE")]; break;
+ }
- [[self navigationItem] setTitle:UCLocalize("SETTINGS")];
-}
+ UpdateExternalStatus(Finish_ == 0 ? 0 : 2);
-- (void) releaseSubviews {
- ignoredCell_ = nil;
- subscribedCell_ = nil;
- table_ = nil;
- ignoredSwitch_ = nil;
- subscribedSwitch_ = nil;
+ [progress_ setRunning:false];
+ [self updateProgress];
- [super releaseSubviews];
+ [self applyRightButton];
}
-- (id) initWithDatabase:(Database *)database package:(NSString *)package {
- if ((self = [super init]) != nil) {
- database_ = database;
- name_ = package;
- } return self;
+- (void) addProgressEvent:(CydiaProgressEvent *)event {
+ [progress_ addEvent:event];
+ [self updateProgress];
}
-- (void) reloadData {
- [super reloadData];
-
- package_ = [database_ packageWithName:name_];
-
- if (package_ != nil) {
- [subscribedSwitch_ setOn:([package_ subscribed] ? 1 : 0) animated:NO];
- [ignoredSwitch_ setOn:([package_ ignored] ? 1 : 0) animated:NO];
- } // XXX: what now, G?
-
- [table_ reloadData];
+- (bool) isProgressCancelled {
+ return cancel_ == 2;
}
-@end
-/* }}} */
-
-/* Installed Controller {{{ */
-@interface InstalledController : FilteredPackageListController {
- BOOL expert_;
+- (void) cancel {
+ cancel_ = 2;
+ [self updateCancel];
}
-- (id) initWithDatabase:(Database *)database;
-
-- (void) updateRoleButton;
-- (void) queueStatusDidChange;
-
-@end
-
-@implementation InstalledController
+- (void) setCancellable:(bool)cancellable {
+ unsigned cancel(cancel_);
-- (NSURL *) referrerURL {
- return [NSURL URLWithString:[NSString stringWithFormat:@"%@/#!/installed/", UI_]];
-}
+ if (!cancellable)
+ cancel_ = 0;
+ else if (cancel_ == 0)
+ cancel_ = 1;
-- (NSURL *) navigationURL {
- return [NSURL URLWithString:@"cydia://installed"];
+ if (cancel != cancel_)
+ [self updateCancel];
}
-- (id) initWithDatabase:(Database *)database {
- if ((self = [super initWithDatabase:database title:UCLocalize("INSTALLED") ]) != nil) {
- [datasource_ addFilter:@"installed" withSelector:@selector(isInstalledAndUnfiltered:) priority:kPackageListFilterPriorityHigh object:[NSNumber numberWithBool:YES]];
- [self updateRoleButton];
- [self queueStatusDidChange];
- } return self;
+- (void) setProgressCancellable:(NSNumber *)cancellable {
+ [self setCancellable:[cancellable boolValue]];
}
-#if !AlwaysReload
-- (void) queueButtonClicked {
- [delegate_ queue];
+- (void) setProgressPercent:(NSNumber *)percent {
+ [progress_ setPercent:[percent floatValue]];
+ [self updateProgress];
}
-#endif
-- (void) queueStatusDidChange {
-#if !AlwaysReload
- if (Queuing_) {
- [[self navigationItem] setLeftBarButtonItem:[[[UIBarButtonItem alloc]
- initWithTitle:UCLocalize("QUEUE")
- style:UIBarButtonItemStyleDone
- target:self
- action:@selector(queueButtonClicked)
- ] autorelease]];
+- (void) setProgressStatus:(NSDictionary *)status {
+ if (status == nil) {
+ [progress_ setCurrent:0];
+ [progress_ setTotal:0];
+ [progress_ setSpeed:0];
} else {
- [[self navigationItem] setLeftBarButtonItem:nil];
- }
-#endif
-}
-
-- (void) updateRoleButton {
- if (Role_ != nil && ![Role_ isEqualToString:@"Developer"])
- [[self navigationItem] setRightBarButtonItem:[[[UIBarButtonItem alloc]
- initWithTitle:(expert_ ? UCLocalize("EXPERT") : UCLocalize("SIMPLE"))
- style:(expert_ ? UIBarButtonItemStyleDone : UIBarButtonItemStylePlain)
- target:self
- action:@selector(roleButtonClicked)
- ] autorelease]];
-}
+ [progress_ setPercent:[[status objectForKey:@"Percent"] floatValue]];
-- (void) roleButtonClicked {
- [datasource_ setObject:[NSNumber numberWithBool:expert_] forFilter:@"installed"];
- [self reloadData];
- expert_ = !expert_;
+ [progress_ setCurrent:[[status objectForKey:@"Current"] floatValue]];
+ [progress_ setTotal:[[status objectForKey:@"Total"] floatValue]];
+ [progress_ setSpeed:[[status objectForKey:@"Speed"] floatValue]];
+ }
- [self updateRoleButton];
+ [self updateProgress];
}
@end
CydiaWriteSources();
}
-// Navigation controller for the queuing badge.
-- (UINavigationController *) queueNavigationController {
- NSArray *controllers = [tabbar_ viewControllers];
- return [controllers objectAtIndex:3];
-}
-
- (void) unloadData {
[tabbar_ unloadData];
}
- (void) _updateData {
[self _saveConfig];
[self unloadData];
-
- UINavigationController *navigation = [self queueNavigationController];
-
- id queuedelegate = nil;
- if ([[navigation viewControllers] count] > 0)
- queuedelegate = [[navigation viewControllers] objectAtIndex:0];
-
- [queuedelegate queueStatusDidChange];
- [[navigation tabBarItem] setBadgeValue:(Queuing_ ? UCLocalize("Q_D") : nil)];
}
- (void) _refreshIfPossible:(NSDate *)update {
_error->Discard();
}
-- (bool) perform {
- // XXX: this is a really crappy way of doing this.
- // like, seriously: this state machine is still broken, and cancelling this here doesn't really /fix/ that.
- // for one, the user can still /start/ a reloading data event while they have a queue, which is stupid
- // for two, this just means there is a race condition between the refresh completing and the confirmation controller appearing.
- if ([tabbar_ updating])
- [tabbar_ cancelUpdate];
-
- if (![database_ prepare])
- return false;
-
- ConfirmationController *page([[[ConfirmationController alloc] initWithDatabase:database_] autorelease]);
- [page setDelegate:self];
- UINavigationController *confirm_([[[UINavigationController alloc] initWithRootViewController:page] autorelease]);
-
- if (IsWildcat_)
- [confirm_ setModalPresentationStyle:UIModalPresentationFormSheet];
- [tabbar_ presentModalViewController:confirm_ animated:YES];
-
- return true;
-}
-
-- (void) queue {
- @synchronized (self) {
- [self perform];
- }
-}
-
- (void) clearPackage:(Package *)package {
@synchronized (self) {
[package clear];
[self resolve];
- [self perform];
}
}
for (Package *package in packages)
[package install];
[self resolve];
- [self perform];
}
}
@synchronized (self) {
[package install];
[self resolve];
- [self perform];
}
}
@synchronized (self) {
[package remove];
[self resolve];
- [self perform];
}
}
@synchronized (self) {
if (![database_ upgrade])
return;
- [self perform];
}
}
}
- (void) confirmWithNavigationController:(UINavigationController *)navigation {
- Queuing_ = false;
++locked_;
[self detachNewProgressSelector:@selector(perform_) toTarget:self forController:navigation title:@"RUNNING"];
--locked_;
}
-- (void) cancelAndClear:(bool)clear {
+- (void) clearQueue {
@synchronized (self) {
- if (clear) {
- [database_ clear];
- Queuing_ = false;
- } else {
- Queuing_ = true;
- }
+ [database_ clear];
[self _updateData];
}
}
[self resolve];
- [self perform];
+ // XXX: [self perform];
}
} else if (button == [alert firstOtherButtonIndex]) {
[broken_ removeAllObjects];
[essential install];
[self resolve];
- [self perform];
+ // XXX: [self perform];
}
} else if (button == [alert firstOtherButtonIndex] + 1) {
[self distUpgrade];
if ([base isEqualToString:@"installed"]) {
controller = [[[InstalledController alloc] initWithDatabase:database_] autorelease];
}
+
+ if ([base isEqualToString:@"queue"]) {
+ controller = [[[ConfirmationController alloc] initWithDatabase:database_] autorelease];
+ }
} else if ([components count] == 2) {
NSString *argument = [components objectAtIndex:1];
[[[UITabBarItem alloc] initWithTitle:UCLocalize("SOURCES") image:[UIImage applicationImageNamed:@"source.png"] tag:0] autorelease],
[[[UITabBarItem alloc] initWithTitle:(AprilFools_ ? @"Timeline" : UCLocalize("CHANGES")) image:[UIImage applicationImageNamed:@"changes.png"] tag:0] autorelease],
[[[UITabBarItem alloc] initWithTitle:UCLocalize("INSTALLED") image:[UIImage applicationImageNamed:@"manage.png"] tag:0] autorelease],
+ [[[UITabBarItem alloc] initWithTitle:UCLocalize("QUEUE") image:[UIImage applicationImageNamed:@"queue.png"] tag:0] autorelease],
nil]);
NSMutableArray *controllers([NSMutableArray array]);
[standard addObject:[NSArray arrayWithObject:@"cydia://sources"]];
[standard addObject:[NSArray arrayWithObject:@"cydia://changes"]];
[standard addObject:[NSArray arrayWithObject:@"cydia://installed"]];
+ [standard addObject:[NSArray arrayWithObject:@"cydia://queue"]];
return standard;
}