Since users have no access to the demo instance, this option is added to make it possible to clean up it in case it is not needed anymore, such as after buying Minecraft.
307 lines
13 KiB
Objective-C
307 lines
13 KiB
Objective-C
#import "LauncherNavigationController.h"
|
|
#import "LauncherPreferences.h"
|
|
#import "LauncherPrefGameDirViewController.h"
|
|
#import "TOInsetGroupedTableView.h"
|
|
#import "ios_uikit_bridge.h"
|
|
#import "utils.h"
|
|
|
|
@interface LauncherPrefGameDirViewController ()<UITextFieldDelegate>
|
|
@property(nonatomic) NSMutableArray *array;
|
|
@end
|
|
|
|
@implementation LauncherPrefGameDirViewController
|
|
|
|
- (void)viewDidLoad
|
|
{
|
|
[super viewDidLoad];
|
|
[self setTitle:localize(@"preference.title.game_directory", nil)];
|
|
|
|
self.array = [[NSMutableArray alloc] init];
|
|
[self.array addObject:@"default"];
|
|
|
|
self.tableView = [[TOInsetGroupedTableView alloc] init];
|
|
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
|
|
self.tableView.sectionFooterHeight = 50;
|
|
|
|
NSString *path = [NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")];
|
|
|
|
NSFileManager *fm = NSFileManager.defaultManager;
|
|
NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil];
|
|
BOOL isDir;
|
|
for (NSString *file in files) {
|
|
[fm fileExistsAtPath:path isDirectory:(&isDir)];
|
|
if (isDir && ![file isEqualToString:@"default"]) {
|
|
[self.array addObject:file];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)changeSelectionTo:(NSString *)name {
|
|
setPreference(@"game_directory", name);
|
|
NSString *multidirPath = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), name];
|
|
NSString *lasmPath = [NSString stringWithFormat:@"%s/Library/Application Support/minecraft", getenv("POJAV_HOME")];
|
|
[NSFileManager.defaultManager removeItemAtPath:lasmPath error:nil];
|
|
[NSFileManager.defaultManager createSymbolicLinkAtPath:lasmPath withDestinationPath:multidirPath error:nil];
|
|
if ([getPreference(@"selected_version_type") intValue] == 0) {
|
|
[(LauncherNavigationController *)self.navigationController reloadVersionList:0];
|
|
}
|
|
}
|
|
|
|
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
|
{
|
|
return self.array.count;
|
|
}
|
|
|
|
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
UITextField *view;
|
|
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
|
|
if (cell == nil) {
|
|
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
|
|
view = [[UITextField alloc] initWithFrame:CGRectMake(20, 10, (cell.bounds.size.width-40)/2, cell.bounds.size.height-20)];
|
|
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
|
|
view.autocorrectionType = UITextAutocorrectionTypeNo;
|
|
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
|
view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
|
view.delegate = self;
|
|
view.returnKeyType = UIReturnKeyDone;
|
|
view.userInteractionEnabled = indexPath.row != 0;
|
|
[cell.contentView addSubview:view];
|
|
}
|
|
view = cell.contentView.subviews.lastObject;
|
|
view.placeholder = self.array[indexPath.row];
|
|
view.text = self.array[indexPath.row];
|
|
|
|
if ([getPreference(@"game_directory") isEqualToString:self.array[indexPath.row]]) {
|
|
cell.accessoryType = UITableViewCellAccessoryCheckmark;
|
|
} else {
|
|
cell.accessoryType = UITableViewCellAccessoryNone;
|
|
}
|
|
|
|
// TODO: display size in the detail label
|
|
return cell;
|
|
}
|
|
|
|
- (UIView *)tableView:(UITableView *)tableView
|
|
viewForFooterInSection:(NSInteger)section
|
|
{
|
|
UITextField *view = [[UITextField alloc] init];
|
|
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
|
|
view.autocorrectionType = UITextAutocorrectionTypeNo;
|
|
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
|
view.delegate = self;
|
|
view.placeholder = localize(@"preference.multidir.add_directory", nil);
|
|
view.returnKeyType = UIReturnKeyDone;
|
|
return view;
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
[self changeSelectionTo:self.array[indexPath.row]];
|
|
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
|
for (int i = 0; i < self.array.count; i++) {
|
|
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
|
|
if (i == indexPath.row) {
|
|
cell.accessoryType = UITableViewCellAccessoryCheckmark;
|
|
} else {
|
|
cell.accessoryType = UITableViewCellAccessoryNone;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
- (UIContextMenuConfiguration *)contextMenuInteraction:(UIContextMenuInteraction *)interaction configurationForMenuAtLocation:(CGPoint)location
|
|
{
|
|
return [UIContextMenuConfiguration configurationWithIdentifier:nil previewProvider:nil actionProvider:^UIMenu * _Nullable(NSArray<UIMenuElement *> * _Nonnull suggestedActions) {
|
|
return [UIMenu menuWithTitle:self.array[indexPath.row] children:@[files, filza, santander]];
|
|
}];
|
|
}
|
|
*/
|
|
|
|
- (id)createOpenScheme:(NSString *)scheme at:(NSString *)directory {
|
|
if (@available(iOS 13.0, *)) {
|
|
return ^(UIAction *action) {
|
|
[UIApplication.sharedApplication
|
|
openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", scheme, directory]]
|
|
options:@{} completionHandler:nil];
|
|
};
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point API_AVAILABLE(ios(13.0))
|
|
{
|
|
// suppress warning
|
|
if (@available(iOS 13.0, *)) {}
|
|
else return nil;
|
|
|
|
NSArray *menuItems;
|
|
NSMutableArray *openItems = [[NSMutableArray alloc] init];
|
|
|
|
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
|
|
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"shareddocuments://"]]) {
|
|
[openItems addObject:[UIAction
|
|
actionWithTitle:@"Files"
|
|
image:nil
|
|
identifier:nil
|
|
handler:[self createOpenScheme:@"shareddocuments" at:directory]]];
|
|
}
|
|
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"filza://"]]) {
|
|
[openItems addObject:[UIAction
|
|
actionWithTitle:@"Filza"
|
|
image:nil
|
|
identifier:nil
|
|
handler:[self createOpenScheme:@"filza" at:directory]]];
|
|
}
|
|
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"santander://"]]) {
|
|
[openItems addObject:[UIAction
|
|
actionWithTitle:@"Santander"
|
|
image:nil
|
|
identifier:nil
|
|
handler:[self createOpenScheme:@"santander" at:directory]]];
|
|
}
|
|
UIMenu *open = [UIMenu
|
|
menuWithTitle:@""
|
|
image:nil
|
|
identifier:nil
|
|
options:UIMenuOptionsDisplayInline
|
|
children:openItems];
|
|
|
|
if (indexPath.row == 0) {
|
|
// You can't delete or rename the default instance, though there will be a reset action (TODO)
|
|
menuItems = @[open];
|
|
} else {
|
|
UIAction *rename = [UIAction
|
|
actionWithTitle:localize(@"Rename", nil)
|
|
image:[UIImage systemImageNamed:@"pencil"]
|
|
identifier:nil
|
|
handler:^(UIAction *action) {
|
|
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
|
|
[view.contentView.subviews.lastObject becomeFirstResponder];
|
|
}
|
|
];
|
|
|
|
UIAction *delete = [UIAction
|
|
actionWithTitle:localize(@"Delete", nil)
|
|
image:[UIImage systemImageNamed:@"trash"]
|
|
identifier:nil
|
|
handler:^(UIAction *action) {
|
|
[self actionDeleteAtIndexPath:indexPath];
|
|
}
|
|
];
|
|
delete.attributes = UIMenuElementAttributesDestructive;
|
|
|
|
menuItems = @[open, rename, delete];
|
|
}
|
|
|
|
return [UIContextMenuConfiguration
|
|
configurationWithIdentifier:nil
|
|
previewProvider:nil
|
|
actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) {
|
|
return [UIMenu menuWithTitle:self.array[indexPath.row] children:menuItems];
|
|
}
|
|
];
|
|
}
|
|
|
|
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (editingStyle == UITableViewCellEditingStyleDelete) {
|
|
[self actionDeleteAtIndexPath:indexPath];
|
|
}
|
|
}
|
|
|
|
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
if (indexPath.row == 0) {
|
|
// TODO: Reset action?
|
|
return UITableViewCellEditingStyleNone;
|
|
} else {
|
|
return UITableViewCellEditingStyleDelete;
|
|
}
|
|
}
|
|
|
|
- (void)actionDeleteAtIndexPath:(NSIndexPath *)indexPath
|
|
{
|
|
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
|
|
NSString *title = localize(@"preference.title.confirm", nil);
|
|
NSString *message = [NSString stringWithFormat:localize(@"preference.title.confirm.delete_game_directory", nil), self.array[indexPath.row]];
|
|
UIAlertController *confirmAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet];
|
|
confirmAlert.popoverPresentationController.sourceView = view;
|
|
confirmAlert.popoverPresentationController.sourceRect = view.bounds;
|
|
UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
|
|
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
|
|
NSError *error;
|
|
if([NSFileManager.defaultManager removeItemAtPath:directory error:&error]) {
|
|
if ([getPreference(@"game_directory") isEqualToString:self.array[indexPath.row]]) {
|
|
[self changeSelectionTo:self.array[0]];
|
|
[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].accessoryType = UITableViewCellAccessoryCheckmark;
|
|
}
|
|
[self.array removeObjectAtIndex:indexPath.row];
|
|
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
|
|
} else {
|
|
showDialog(self, localize(@"Error", nil), error.localizedDescription);
|
|
}
|
|
}];
|
|
UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil];
|
|
[confirmAlert addAction:cancel];
|
|
[confirmAlert addAction:ok];
|
|
[self presentViewController:confirmAlert animated:YES completion:nil];
|
|
}
|
|
|
|
#pragma mark UITextField
|
|
|
|
- (void)textFieldDidEndEditing:(UITextField *)sender {
|
|
BOOL isFooterView = sender.superview == self.tableView;
|
|
if (!sender.hasText || [sender.text isEqualToString:sender.placeholder]) {
|
|
if (isFooterView) {
|
|
return;
|
|
}
|
|
sender.text = sender.placeholder;
|
|
return;
|
|
}
|
|
|
|
NSError *error;
|
|
|
|
NSString *dest = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.text];
|
|
if (isFooterView) {
|
|
[NSFileManager.defaultManager createDirectoryAtPath:dest withIntermediateDirectories:NO attributes:nil error:&error];
|
|
} else {
|
|
NSString *source = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.placeholder];
|
|
[NSFileManager.defaultManager moveItemAtPath:source toPath:dest error:&error];
|
|
}
|
|
|
|
if (error == nil) {
|
|
[self changeSelectionTo:sender.text];
|
|
if (isFooterView) {
|
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.array.count inSection:0];
|
|
[self.array addObject:sender.text];
|
|
[self.tableView beginUpdates];
|
|
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
|
|
[self.tableView endUpdates];
|
|
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
|
|
// Clear text
|
|
sender.text = @"";
|
|
} else {
|
|
int index = [self.array indexOfObject:sender.placeholder];
|
|
self.array[index] = sender.placeholder = sender.text;
|
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
|
|
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
|
|
}
|
|
} else {
|
|
// Restore to the previous name if we encounter an error
|
|
if (!isFooterView) {
|
|
sender.text = sender.placeholder;
|
|
}
|
|
showDialog(self, localize(@"Error", nil), error.localizedDescription);
|
|
}
|
|
}
|
|
|
|
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
|
|
[textField invalidateIntrinsicContentSize];
|
|
CGRect frame = textField.frame;
|
|
frame.size.width = MAX(50, textField.intrinsicContentSize.width + 10);
|
|
textField.frame = frame;
|
|
return YES;
|
|
}
|
|
|
|
@end
|