Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/PojavLauncher_iOS
Path: blob/main/Natives/LauncherPrefGameDirViewController.m
589 views
1
#import "LauncherNavigationController.h"
2
#import "LauncherPreferences.h"
3
#import "LauncherPrefGameDirViewController.h"
4
#import "NSFileManager+NRFileManager.h"
5
#import "PLProfiles.h"
6
#import "ios_uikit_bridge.h"
7
#import "utils.h"
8
9
@interface LauncherPrefGameDirViewController ()<UITextFieldDelegate>
10
@property(nonatomic) NSMutableArray *array;
11
@end
12
13
@implementation LauncherPrefGameDirViewController
14
15
- (void)viewDidLoad
16
{
17
[super viewDidLoad];
18
[self setTitle:localize(@"preference.title.game_directory", nil)];
19
20
self.array = [[NSMutableArray alloc] init];
21
[self.array addObject:@"default"];
22
23
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
24
self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
25
self.tableView.sectionFooterHeight = 50;
26
27
NSString *path = [NSString stringWithFormat:@"%s/instances", getenv("POJAV_HOME")];
28
29
NSFileManager *fm = NSFileManager.defaultManager;
30
NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil];
31
BOOL isDir;
32
for (NSString *file in files) {
33
[fm fileExistsAtPath:path isDirectory:(&isDir)];
34
if (isDir && ![file isEqualToString:@"default"]) {
35
[self.array addObject:file];
36
}
37
}
38
}
39
40
- (void)changeSelectionTo:(NSString *)name {
41
if (getenv("DEMO_LOCK")) return;
42
43
setPrefObject(@"general.game_directory", name);
44
NSString *multidirPath = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), name];
45
NSString *lasmPath = @(getenv("POJAV_GAME_DIR"));
46
[NSFileManager.defaultManager removeItemAtPath:lasmPath error:nil];
47
[NSFileManager.defaultManager createSymbolicLinkAtPath:lasmPath withDestinationPath:multidirPath error:nil];
48
[NSFileManager.defaultManager changeCurrentDirectoryPath:lasmPath];
49
toggleIsolatedPref(NO);
50
[self.navigationController performSelector:@selector(reloadProfileList)];
51
}
52
53
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
54
{
55
return self.array.count;
56
}
57
58
- (UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
59
{
60
UITextField *view;
61
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
62
if (cell == nil) {
63
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
64
view = [[UITextField alloc] initWithFrame:CGRectMake(20, 10, (cell.bounds.size.width-40)/2, cell.bounds.size.height-20)];
65
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
66
view.autocorrectionType = UITextAutocorrectionTypeNo;
67
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
68
view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
69
view.delegate = self;
70
view.returnKeyType = UIReturnKeyDone;
71
view.userInteractionEnabled = indexPath.row != 0;
72
[cell.contentView addSubview:view];
73
cell.detailTextLabel.text = @"...";
74
}
75
view = cell.contentView.subviews.firstObject;
76
view.placeholder = self.array[indexPath.row];
77
view.text = self.array[indexPath.row];
78
cell.textLabel.hidden = YES;
79
cell.textLabel.text = view.text;
80
81
// Calculate the instance size
82
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
83
unsigned long long folderSize = 0;
84
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
85
[NSFileManager.defaultManager nr_getAllocatedSize:&folderSize ofDirectoryAtURL:[NSURL fileURLWithPath:directory] error:nil];
86
dispatch_async(dispatch_get_main_queue(), ^{
87
cell.detailTextLabel.text = [NSByteCountFormatter stringFromByteCount:folderSize countStyle:NSByteCountFormatterCountStyleMemory];
88
});
89
});
90
91
if ([getPrefObject(@"general.game_directory") isEqualToString:self.array[indexPath.row]]) {
92
cell.accessoryType = UITableViewCellAccessoryCheckmark;
93
} else {
94
cell.accessoryType = UITableViewCellAccessoryNone;
95
}
96
97
return cell;
98
}
99
100
- (UIView *)tableView:(UITableView *)tableView
101
viewForFooterInSection:(NSInteger)section
102
{
103
UITextField *view = [[UITextField alloc] init];
104
[view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
105
view.autocorrectionType = UITextAutocorrectionTypeNo;
106
view.autocapitalizationType = UITextAutocapitalizationTypeNone;
107
view.delegate = self;
108
view.placeholder = localize(@"preference.multidir.add_directory", nil);
109
view.returnKeyType = UIReturnKeyDone;
110
return view;
111
}
112
113
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
114
[self changeSelectionTo:self.array[indexPath.row]];
115
[tableView deselectRowAtIndexPath:indexPath animated:NO];
116
for (int i = 0; i < self.array.count; i++) {
117
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
118
if (i == indexPath.row) {
119
cell.accessoryType = UITableViewCellAccessoryCheckmark;
120
} else {
121
cell.accessoryType = UITableViewCellAccessoryNone;
122
}
123
}
124
}
125
126
- (id)createOpenScheme:(NSString *)scheme at:(NSString *)directory {
127
return ^(UIAction *action) {
128
[UIApplication.sharedApplication
129
openURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", scheme, directory]]
130
options:@{} completionHandler:nil];
131
};
132
}
133
134
- (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point
135
{
136
NSArray *menuItems;
137
NSMutableArray *openItems = [[NSMutableArray alloc] init];
138
139
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
140
NSDictionary *apps = @{
141
@"shareddocuments": @"Files",
142
@"filza": @"Filza",
143
@"santander": @"Santander",
144
};
145
for (NSString *key in apps.allKeys) {
146
NSString *url = [NSString stringWithFormat:@"%@://", key];
147
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:url]]) {
148
[openItems addObject:[UIAction
149
actionWithTitle:apps[key]
150
image:nil
151
identifier:nil
152
handler:[self createOpenScheme:key at:directory]]];
153
}
154
}
155
UIMenu *open = [UIMenu
156
menuWithTitle:@""
157
image:nil
158
identifier:nil
159
options:UIMenuOptionsDisplayInline
160
children:openItems];
161
162
if (indexPath.row == 0) {
163
// You can't delete or rename the default instance, though there will be a reset action (TODO)
164
menuItems = @[open];
165
} else {
166
UIAction *rename = [UIAction
167
actionWithTitle:localize(@"Rename", nil)
168
image:[UIImage systemImageNamed:@"pencil"]
169
identifier:nil
170
handler:^(UIAction *action) {
171
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
172
[view.contentView.subviews.firstObject becomeFirstResponder];
173
}
174
];
175
176
UIAction *delete = [UIAction
177
actionWithTitle:localize(@"Delete", nil)
178
image:[UIImage systemImageNamed:@"trash"]
179
identifier:nil
180
handler:^(UIAction *action) {
181
[self actionDeleteAtIndexPath:indexPath];
182
}
183
];
184
delete.attributes = UIMenuElementAttributesDestructive;
185
186
menuItems = @[open, rename, delete];
187
}
188
189
return [UIContextMenuConfiguration
190
configurationWithIdentifier:nil
191
previewProvider:nil
192
actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) {
193
return [UIMenu menuWithTitle:self.array[indexPath.row] children:menuItems];
194
}
195
];
196
}
197
198
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
199
{
200
if (editingStyle == UITableViewCellEditingStyleDelete) {
201
[self actionDeleteAtIndexPath:indexPath];
202
}
203
}
204
205
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
206
{
207
if (indexPath.row == 0) {
208
// TODO: Reset action?
209
return UITableViewCellEditingStyleNone;
210
} else {
211
return UITableViewCellEditingStyleDelete;
212
}
213
}
214
215
- (void)actionDeleteAtIndexPath:(NSIndexPath *)indexPath
216
{
217
UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath];
218
NSString *title = localize(@"preference.title.confirm", nil);
219
NSString *message = [NSString stringWithFormat:localize(@"preference.title.confirm.delete_game_directory", nil), self.array[indexPath.row]];
220
UIAlertController *confirmAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet];
221
confirmAlert.popoverPresentationController.sourceView = view;
222
confirmAlert.popoverPresentationController.sourceRect = view.bounds;
223
UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
224
NSString *directory = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), self.array[indexPath.row]];
225
NSError *error;
226
if([NSFileManager.defaultManager removeItemAtPath:directory error:&error]) {
227
if ([getPrefObject(@"general.game_directory") isEqualToString:self.array[indexPath.row]]) {
228
[self changeSelectionTo:self.array[0]];
229
[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].accessoryType = UITableViewCellAccessoryCheckmark;
230
}
231
[self.array removeObjectAtIndex:indexPath.row];
232
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
233
} else {
234
showDialog(localize(@"Error", nil), error.localizedDescription);
235
}
236
}];
237
UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil];
238
[confirmAlert addAction:cancel];
239
[confirmAlert addAction:ok];
240
[self presentViewController:confirmAlert animated:YES completion:nil];
241
}
242
243
- (void) dismissModalViewController {
244
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
245
}
246
247
#pragma mark UITextField
248
249
- (void)textFieldDidEndEditing:(UITextField *)sender {
250
BOOL isFooterView = sender.superview == self.tableView;
251
if (!sender.hasText || [sender.text isEqualToString:sender.placeholder]) {
252
if (isFooterView) {
253
return;
254
}
255
sender.text = sender.placeholder;
256
return;
257
}
258
259
NSError *error;
260
261
NSString *dest = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.text];
262
if (isFooterView) {
263
[NSFileManager.defaultManager createDirectoryAtPath:dest withIntermediateDirectories:NO attributes:nil error:&error];
264
} else {
265
NSString *source = [NSString stringWithFormat:@"%s/instances/%@", getenv("POJAV_HOME"), sender.placeholder];
266
[NSFileManager.defaultManager moveItemAtPath:source toPath:dest error:&error];
267
}
268
269
if (error == nil) {
270
[self changeSelectionTo:sender.text];
271
if (isFooterView) {
272
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.array.count inSection:0];
273
[self.array addObject:sender.text];
274
[self.tableView beginUpdates];
275
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
276
[self.tableView endUpdates];
277
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
278
// Clear text
279
sender.text = @"";
280
} else {
281
int index = [self.array indexOfObject:sender.placeholder];
282
self.array[index] = sender.placeholder = sender.text;
283
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
284
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
285
}
286
} else {
287
// Restore to the previous name if we encounter an error
288
if (!isFooterView) {
289
sender.text = sender.placeholder;
290
}
291
showDialog(localize(@"Error", nil), error.localizedDescription);
292
}
293
}
294
295
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
296
[textField invalidateIntrinsicContentSize];
297
CGRect frame = textField.frame;
298
frame.size.width = MAX(50, textField.intrinsicContentSize.width + 10);
299
textField.frame = frame;
300
return YES;
301
}
302
303
@end
304
305