CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/MemStickScreen.cpp
Views: 1401
1
// Copyright (c) 2013- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "ppsspp_config.h"
19
20
#include "android/jni/app-android.h"
21
22
#include "Common/Log.h"
23
#include "Common/UI/UI.h"
24
#include "Common/UI/View.h"
25
#include "Common/UI/ViewGroup.h"
26
27
#include "Common/StringUtils.h"
28
#include "Common/System/System.h"
29
#include "Common/System/Request.h"
30
#include "Common/System/NativeApp.h"
31
#include "Common/System/Display.h"
32
#include "Common/System/OSD.h"
33
#include "Common/Data/Text/I18n.h"
34
#include "Common/Data/Text/Parsers.h"
35
36
#include "Common/File/AndroidStorage.h"
37
#include "Common/File/FileUtil.h"
38
#include "Common/File/Path.h"
39
#include "Common/File/DiskFree.h"
40
41
#include "Common/Thread/ThreadManager.h"
42
43
#include "Core/Config.h"
44
#include "Core/Reporting.h"
45
#include "Core/System.h"
46
#include "Core/Util/GameManager.h"
47
#include "Core/Util/MemStick.h"
48
49
#include "UI/MemStickScreen.h"
50
#include "UI/MainScreen.h"
51
#include "UI/MiscScreens.h"
52
#include "UI/OnScreenDisplay.h"
53
54
static std::string FormatSpaceString(int64_t space) {
55
if (space >= 0) {
56
char buffer[50];
57
NiceSizeFormat(space, buffer, sizeof(buffer));
58
return buffer;
59
} else {
60
return "N/A";
61
}
62
}
63
64
MemStickScreen::MemStickScreen(bool initialSetup)
65
: initialSetup_(initialSetup) {
66
#if PPSSPP_PLATFORM(ANDROID)
67
// Let's only offer the browse-for-folder choice on Android 10 or later.
68
// Earlier versions often don't really have working folder browsers.
69
storageBrowserWorking_ = System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 29;
70
#else
71
// For testing UI only
72
storageBrowserWorking_ = true;
73
#endif
74
75
if (initialSetup_) {
76
// Preselect current choice.
77
if (System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) {
78
choice_ = CHOICE_BROWSE_FOLDER;
79
} else {
80
WARN_LOG_REPORT(Log::System, "Scoped storage not enabled - shouldn't be in MemStickScreen at initial setup");
81
choice_ = CHOICE_STORAGE_ROOT;
82
// Shouldn't really be here in initial setup.
83
}
84
} else {
85
// Detect the current choice, so it's preselected in the UI.
86
#if PPSSPP_PLATFORM(UWP)
87
if (g_Config.memStickDirectory == g_Config.internalDataDirectory) {
88
#else
89
if (g_Config.memStickDirectory == Path(g_extFilesDir)) {
90
#endif
91
choice_ = CHOICE_PRIVATE_DIRECTORY;
92
} else if (g_Config.memStickDirectory == Path(g_externalDir)) {
93
choice_ = CHOICE_STORAGE_ROOT;
94
} else if (storageBrowserWorking_) {
95
choice_ = CHOICE_BROWSE_FOLDER;
96
} else {
97
choice_ = CHOICE_SET_MANUAL;
98
}
99
}
100
}
101
102
static void AddExplanation(UI::ViewGroup *viewGroup, MemStickScreen::Choice choice, UI::View *extraView = nullptr) {
103
auto iz = GetI18NCategory(I18NCat::MEMSTICK);
104
using namespace UI;
105
106
int flags = FLAG_WRAP_TEXT;
107
108
UI::ViewGroup *holder = new UI::LinearLayout(ORIENT_VERTICAL);
109
110
UI::ViewGroup *indentHolder = new UI::LinearLayout(ORIENT_HORIZONTAL);
111
indentHolder->Add(new Spacer(20.0));
112
indentHolder->Add(holder);
113
114
viewGroup->Add(indentHolder);
115
116
if (extraView) {
117
holder->Add(extraView);
118
}
119
120
switch (choice) {
121
case MemStickScreen::CHOICE_STORAGE_ROOT:
122
// Old school choice
123
holder->Add(new TextView(iz->T("DataWillStay", "Data will stay even if you uninstall PPSSPP"), flags, false))->SetBullet(true);
124
holder->Add(new TextView(iz->T("DataCanBeShared", "Data can be shared between PPSSPP regular/Gold"), flags, false))->SetBullet(true);
125
holder->Add(new TextView(iz->T("EasyUSBAccess", "Easy USB access"), flags, false))->SetBullet(true);
126
break;
127
case MemStickScreen::CHOICE_BROWSE_FOLDER:
128
holder->Add(new TextView(iz->T("DataWillStay", "Data will stay even if you uninstall PPSSPP"), flags, false))->SetBullet(true);
129
holder->Add(new TextView(iz->T("DataCanBeShared", "Data can be shared between PPSSPP regular/Gold"), flags, false))->SetBullet(true);
130
#if !PPSSPP_PLATFORM(UWP)
131
holder->Add(new TextView(iz->T("EasyUSBAccess", "Easy USB access"), flags, false))->SetBullet(true);
132
#endif
133
break;
134
case MemStickScreen::CHOICE_PRIVATE_DIRECTORY:
135
// Consider https://www.compart.com/en/unicode/U+26A0 (unicode warning sign?)? or a graphic?
136
holder->Add(new TextView(iz->T("DataWillBeLostOnUninstall", "Warning! Data will be lost when you uninstall PPSSPP!"), flags, false))->SetBullet(true);
137
holder->Add(new TextView(iz->T("DataCannotBeShared", "Data CANNOT be shared between PPSSPP regular/Gold!"), flags, false))->SetBullet(true);
138
#if !PPSSPP_PLATFORM(UWP)
139
#if GOLD
140
holder->Add(new TextView(iz->T("USBAccessThroughGold", "USB access through Android/data/org.ppsspp.ppssppgold/files"), flags, false))->SetBullet(true);
141
#else
142
holder->Add(new TextView(iz->T("USBAccessThrough", "USB access through Android/data/org.ppsspp.ppsspp/files"), flags, false))->SetBullet(true);
143
#endif
144
#endif
145
break;
146
case MemStickScreen::CHOICE_SET_MANUAL:
147
default:
148
holder->Add(new TextView(iz->T("EasyUSBAccess", "Easy USB access"), flags, false))->SetBullet(true);
149
// What more?
150
151
// Should we have a special text here? It'll popup a text window for editing.
152
break;
153
}
154
}
155
156
void MemStickScreen::CreateViews() {
157
using namespace UI;
158
159
auto di = GetI18NCategory(I18NCat::DIALOG);
160
auto ms = GetI18NCategory(I18NCat::MEMSTICK);
161
162
Margins actionMenuMargins(15, 0, 15, 0);
163
164
root_ = new LinearLayout(ORIENT_HORIZONTAL);
165
166
Spacer *spacerColumn = new Spacer(new LinearLayoutParams(20.0, FILL_PARENT, 0.0f));
167
ScrollView *mainColumnScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
168
169
ViewGroup *mainColumn = new LinearLayoutList(ORIENT_VERTICAL);
170
mainColumnScroll->Add(mainColumn);
171
172
root_->Add(spacerColumn);
173
root_->Add(mainColumnScroll);
174
175
if (initialSetup_) {
176
mainColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f)));
177
mainColumn->Add(new TextView(ms->T("Welcome to PPSSPP!"), ALIGN_LEFT, false));
178
}
179
180
mainColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 18.0f, 0.0f)));
181
182
mainColumn->Add(new TextView(ms->T("MemoryStickDescription", "Choose where to keep PSP data (Memory Stick)"), ALIGN_LEFT, false));
183
mainColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 18.0f, 0.0f)));
184
185
ViewGroup *subColumns = new LinearLayoutList(ORIENT_HORIZONTAL);
186
mainColumn->Add(subColumns);
187
188
ViewGroup *leftColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
189
subColumns->Add(leftColumn);
190
191
ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(220, FILL_PARENT, actionMenuMargins));
192
subColumns->Add(rightColumnItems);
193
194
// For legacy Android systems, so you can switch back to the old ways if you move to SD or something.
195
// Trying to avoid needing a scroll view, so only showing the explanation for one option at a time.
196
#if !PPSSPP_PLATFORM(UWP)
197
if (!System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE)) {
198
leftColumn->Add(new RadioButton(&choice_, CHOICE_STORAGE_ROOT, ms->T("Use PSP folder at root of storage")))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);
199
if (choice_ == CHOICE_STORAGE_ROOT) {
200
AddExplanation(leftColumn, (MemStickScreen::Choice)choice_);
201
}
202
}
203
#endif
204
205
if (storageBrowserWorking_) {
206
leftColumn->Add(new RadioButton(&choice_, CHOICE_BROWSE_FOLDER, ms->T("Create or Choose a PSP folder")))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);
207
208
// TODO: Show current folder here if we have one set.
209
} else {
210
leftColumn->Add(new RadioButton(&choice_, CHOICE_SET_MANUAL, ms->T("Manually specify PSP folder")))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);
211
leftColumn->Add(new TextView(ms->T("DataWillStay", "Data will stay even if you uninstall PPSSPP.")))->SetBullet(true);
212
leftColumn->Add(new TextView(ms->T("DataCanBeShared", "Data can be shared between PPSSPP regular/Gold.")))->SetBullet(true);
213
// TODO: Show current folder here if we have one set.
214
}
215
errorNoticeView_ = leftColumn->Add(new NoticeView(NoticeLevel::WARN, ms->T("Cancelled - try again"), ""));
216
errorNoticeView_->SetVisibility(UI::V_GONE);
217
218
if (choice_ == CHOICE_BROWSE_FOLDER || choice_ == CHOICE_SET_MANUAL) {
219
UI::View *extraView = nullptr;
220
if (!g_Config.memStickDirectory.empty()) {
221
extraView = new TextView(StringFromFormat(" %s: %s", ms->T_cstr("Current"), g_Config.memStickDirectory.ToVisualString().c_str()), ALIGN_LEFT, false);
222
}
223
AddExplanation(leftColumn, (MemStickScreen::Choice)choice_, extraView);
224
}
225
226
std::string privateString(ms->T("Use App Private Data"));
227
228
if (initialSetup_) {
229
privateString = StringFromFormat("%s (%s)", ms->T_cstr("Skip for now"), privateString.c_str());
230
}
231
232
leftColumn->Add(new RadioButton(&choice_, CHOICE_PRIVATE_DIRECTORY, privateString))->OnClick.Handle(this, &MemStickScreen::OnChoiceClick);
233
if (choice_ == CHOICE_PRIVATE_DIRECTORY) {
234
AddExplanation(leftColumn, (MemStickScreen::Choice)choice_);
235
}
236
237
leftColumn->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f)));
238
239
std::string_view confirmButtonText = "";
240
ImageID confirmButtonImage = ImageID::invalid();
241
switch (choice_) {
242
case CHOICE_BROWSE_FOLDER:
243
confirmButtonText = di->T("OK");
244
confirmButtonImage = ImageID("I_FOLDER_OPEN");
245
break;
246
case CHOICE_PRIVATE_DIRECTORY:
247
if (initialSetup_) {
248
confirmButtonText = di->T("Skip");
249
confirmButtonImage = ImageID("I_WARNING");
250
} else {
251
confirmButtonText = di->T("OK");
252
}
253
break;
254
case CHOICE_STORAGE_ROOT:
255
case CHOICE_SET_MANUAL:
256
default:
257
confirmButtonText = di->T("OK");
258
break;
259
}
260
261
rightColumnItems->Add(new UI::Choice(confirmButtonText, confirmButtonImage))->OnClick.Handle<MemStickScreen>(this, &MemStickScreen::OnConfirmClick);
262
rightColumnItems->Add(new Spacer(new LinearLayoutParams(FILL_PARENT, 12.0f, 0.0f)));
263
264
if (!initialSetup_) {
265
rightColumnItems->Add(new UI::Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
266
}
267
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_TV) {
268
rightColumnItems->Add(new UI::Choice(ms->T("WhatsThis", "What's this?")))->OnClick.Handle<MemStickScreen>(this, &MemStickScreen::OnHelp);
269
}
270
271
INFO_LOG(Log::System, "MemStickScreen: initialSetup=%d", (int)initialSetup_);
272
}
273
274
UI::EventReturn MemStickScreen::OnHelp(UI::EventParams &params) {
275
// I'm letting the old redirect handle this one, as the target is within /docs on the website,
276
// and that structure may change a bit.
277
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/guide_storage.html");
278
279
return UI::EVENT_DONE;
280
}
281
282
UI::EventReturn MemStickScreen::OnChoiceClick(UI::EventParams &params) {
283
// Change the confirm button to match the choice,
284
// and change the text that we show.
285
RecreateViews();
286
return UI::EVENT_DONE;
287
}
288
289
290
UI::EventReturn MemStickScreen::OnConfirmClick(UI::EventParams &params) {
291
switch (choice_) {
292
case CHOICE_SET_MANUAL:
293
return SetFolderManually(params);
294
case CHOICE_STORAGE_ROOT:
295
return UseStorageRoot(params);
296
case CHOICE_PRIVATE_DIRECTORY:
297
return UseInternalStorage(params);
298
case CHOICE_BROWSE_FOLDER:
299
return Browse(params);
300
}
301
return UI::EVENT_DONE;
302
}
303
304
UI::EventReturn MemStickScreen::SetFolderManually(UI::EventParams &params) {
305
// The old way, from before scoped storage. Write in the full path.
306
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(SWITCH)
307
auto sy = GetI18NCategory(I18NCat::SYSTEM);
308
System_InputBoxGetString(GetRequesterToken(), sy->T("Memory Stick Folder"), g_Config.memStickDirectory.ToString(), false, [&](const std::string &value, int) {
309
auto sy = GetI18NCategory(I18NCat::SYSTEM);
310
auto di = GetI18NCategory(I18NCat::DIALOG);
311
312
std::string newPath = value;
313
size_t pos = newPath.find_last_not_of("/");
314
// Gotta have at least something but a /, and also needs to start with a /.
315
if (newPath.empty() || pos == newPath.npos || newPath[0] != '/') {
316
settingInfo_->Show(sy->T("ChangingMemstickPathInvalid", "That path couldn't be used to save Memory Stick files."), nullptr);
317
return;
318
}
319
if (pos != newPath.size() - 1) {
320
newPath = newPath.substr(0, pos + 1);
321
}
322
323
if (newPath.empty()) {
324
// Reuse below message instead of adding yet another string.
325
System_Toast(sy->T("Path does not exist!"));
326
return;
327
}
328
329
Path pendingMemStickFolder(newPath);
330
331
if (!File::Exists(pendingMemStickFolder)) {
332
// Try to fix the path string, apparently some users got used to leaving out the /.
333
if (newPath[0] != '/') {
334
newPath = "/" + newPath;
335
}
336
337
pendingMemStickFolder = Path(newPath);
338
}
339
340
if (!File::Exists(pendingMemStickFolder) && pendingMemStickFolder.Type() == PathType::NATIVE) {
341
// Still no path? Try to automatically fix the case.
342
std::string oldNewPath = newPath;
343
FixPathCase(Path(""), newPath, FixPathCaseBehavior::FPC_FILE_MUST_EXIST);
344
if (oldNewPath != newPath) {
345
NOTICE_LOG(Log::IO, "Fixed path case: %s -> %s", oldNewPath.c_str(), newPath.c_str());
346
pendingMemStickFolder = Path(newPath);
347
} else {
348
NOTICE_LOG(Log::IO, "Failed to fix case of path %s (result: %s)", newPath.c_str(), oldNewPath.c_str());
349
}
350
}
351
352
if (pendingMemStickFolder == g_Config.memStickDirectory) {
353
// Same directory as before - all good. Nothing to do.
354
TriggerFinish(DialogResult::DR_OK);
355
return;
356
}
357
358
if (!File::Exists(pendingMemStickFolder)) {
359
System_Toast(sy->T("Path does not exist!"));
360
return;
361
}
362
363
screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, false));
364
});
365
#endif
366
return UI::EVENT_DONE;
367
}
368
369
UI::EventReturn MemStickScreen::UseInternalStorage(UI::EventParams &params) {
370
#if PPSSPP_PLATFORM(UWP)
371
Path pendingMemStickFolder = g_Config.internalDataDirectory;
372
#else
373
Path pendingMemStickFolder = Path(g_extFilesDir);
374
#endif
375
376
if (initialSetup_) {
377
// There's not gonna be any files here in this case since it's a fresh install.
378
// Let's just accept it and move on. No need to move files either.
379
if (SwitchMemstickFolderTo(pendingMemStickFolder)) {
380
TriggerFinish(DialogResult::DR_OK);
381
} else {
382
// This can't really happen?? Not worth making an error message.
383
ERROR_LOG_REPORT(Log::System, "Could not switch memstick path in setup (internal)");
384
}
385
// Don't have a confirmation dialog that would otherwise do it for us, need to just switch directly to the main screen.
386
screenManager()->switchScreen(new MainScreen());
387
} else if (pendingMemStickFolder != g_Config.memStickDirectory) {
388
// Always ask for confirmation when called from the UI. Likely there's already some data.
389
screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, false));
390
} else {
391
// User chose the same directory it's already in. Let's just bail.
392
TriggerFinish(DialogResult::DR_OK);
393
}
394
return UI::EVENT_DONE;
395
}
396
397
UI::EventReturn MemStickScreen::UseStorageRoot(UI::EventParams &params) {
398
Path pendingMemStickFolder = Path(g_externalDir);
399
400
if (initialSetup_) {
401
// There's not gonna be any files here in this case since it's a fresh install.
402
// Let's just accept it and move on. No need to move files either.
403
if (SwitchMemstickFolderTo(pendingMemStickFolder)) {
404
TriggerFinish(DialogResult::DR_OK);
405
} else {
406
// This can't really happen?? Not worth making an error message.
407
ERROR_LOG_REPORT(Log::System, "Could not switch memstick path in setup");
408
}
409
} else if (pendingMemStickFolder != g_Config.memStickDirectory) {
410
// Always ask for confirmation when called from the UI. Likely there's already some data.
411
screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, false));
412
} else {
413
// User chose the same directory it's already in. Let's just bail.
414
TriggerFinish(DialogResult::DR_OK);
415
}
416
return UI::EVENT_DONE;
417
}
418
419
UI::EventReturn MemStickScreen::Browse(UI::EventParams &params) {
420
auto mm = GetI18NCategory(I18NCat::MAINMENU);
421
System_BrowseForFolder(GetRequesterToken(), mm->T("Choose folder"), g_Config.memStickDirectory, [=](const std::string &value, int) {
422
Path pendingMemStickFolder = Path(value);
423
INFO_LOG(Log::System, "Got folder: '%s' (old: %s)", pendingMemStickFolder.c_str(), g_Config.memStickDirectory.c_str());
424
// Browse finished. Let's pop up the confirmation dialog.
425
if (!pendingMemStickFolder.empty() && pendingMemStickFolder == g_Config.memStickDirectory && File::IsDirectory(pendingMemStickFolder)) {
426
auto di = GetI18NCategory(I18NCat::DIALOG);
427
// Not sure how this could happen, but let's go with it.
428
g_OSD.Show(OSDType::MESSAGE_SUCCESS, di->T("Done!"));
429
done_ = true;
430
return;
431
}
432
errorNoticeView_->SetVisibility(UI::V_GONE);
433
434
screenManager()->push(new ConfirmMemstickMoveScreen(pendingMemStickFolder, initialSetup_));
435
}, [=]() {
436
errorNoticeView_->SetVisibility(UI::V_VISIBLE);
437
});
438
return UI::EVENT_DONE;
439
}
440
441
void MemStickScreen::dialogFinished(const Screen *dialog, DialogResult result) {
442
if (result == DialogResult::DR_OK) {
443
INFO_LOG(Log::System, "Confirmation screen done - moving on.");
444
// There's a screen manager bug if we call TriggerFinish directly.
445
// Can't be bothered right now, so we pick this up in update().
446
done_ = true;
447
}
448
// otherwise, we just keep going.
449
}
450
451
void MemStickScreen::update() {
452
UIDialogScreenWithBackground::update();
453
if (done_) {
454
TriggerFinish(DialogResult::DR_OK);
455
done_ = false;
456
}
457
}
458
459
ConfirmMemstickMoveScreen::ConfirmMemstickMoveScreen(Path newMemstickFolder, bool initialSetup)
460
: newMemstickFolder_(newMemstickFolder), initialSetup_(initialSetup), progressReporter_() {
461
existingFilesInNewFolder_ = FolderSeemsToBeUsed(newMemstickFolder);
462
if (initialSetup_) {
463
moveData_ = false;
464
}
465
}
466
467
ConfirmMemstickMoveScreen::~ConfirmMemstickMoveScreen() {
468
if (moveDataTask_) {
469
INFO_LOG(Log::System, "Move Data task still running, blocking on it");
470
moveDataTask_->BlockUntilReady();
471
delete moveDataTask_;
472
}
473
}
474
475
void ConfirmMemstickMoveScreen::CreateViews() {
476
using namespace UI;
477
auto sy = GetI18NCategory(I18NCat::SYSTEM);
478
auto ms = GetI18NCategory(I18NCat::MEMSTICK);
479
480
root_ = new LinearLayout(ORIENT_HORIZONTAL);
481
482
Path oldMemstickFolder = g_Config.memStickDirectory;
483
484
Spacer *spacerColumn = new Spacer(new LinearLayoutParams(20.0, FILL_PARENT, 0.0f));
485
ViewGroup *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
486
ViewGroup *rightColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
487
root_->Add(spacerColumn);
488
root_->Add(leftColumn);
489
root_->Add(rightColumn);
490
491
int64_t freeSpaceNew;
492
int64_t freeSpaceOld;
493
free_disk_space(newMemstickFolder_, freeSpaceNew);
494
free_disk_space(oldMemstickFolder, freeSpaceOld);
495
496
leftColumn->Add(new TextView(ms->T("Selected PSP Data Folder"), ALIGN_LEFT, false));
497
if (!initialSetup_) {
498
leftColumn->Add(new NoticeView(NoticeLevel::WARN, ms->T("PPSSPP will restart after the change"), ""));
499
}
500
leftColumn->Add(new TextView(newMemstickFolder_.ToVisualString(), ALIGN_LEFT, false));
501
std::string newFreeSpaceText = std::string(ms->T("Free space")) + ": " + FormatSpaceString(freeSpaceNew);
502
leftColumn->Add(new TextView(newFreeSpaceText, ALIGN_LEFT, false));
503
if (existingFilesInNewFolder_) {
504
leftColumn->Add(new NoticeView(NoticeLevel::SUCCESS, ms->T("Already contains PSP data"), ""));
505
if (!moveData_) {
506
leftColumn->Add(new NoticeView(NoticeLevel::INFO, ms->T("No data will be changed"), ""));
507
}
508
}
509
if (!error_.empty()) {
510
leftColumn->Add(new TextView(error_, ALIGN_LEFT, false));
511
}
512
513
if (!oldMemstickFolder.empty()) {
514
std::string oldFreeSpaceText = std::string(ms->T("Free space")) + ": " + FormatSpaceString(freeSpaceOld);
515
516
rightColumn->Add(new TextView(std::string(ms->T("Current")) + ":", ALIGN_LEFT, false));
517
rightColumn->Add(new TextView(oldMemstickFolder.ToVisualString(), ALIGN_LEFT, false));
518
rightColumn->Add(new TextView(oldFreeSpaceText, ALIGN_LEFT, false));
519
}
520
521
if (moveDataTask_) {
522
progressView_ = leftColumn->Add(new TextView(progressReporter_.Format()));
523
} else {
524
progressView_ = nullptr;
525
}
526
527
if (!moveDataTask_) {
528
if (!initialSetup_) {
529
leftColumn->Add(new CheckBox(&moveData_, ms->T("Move Data")))->OnClick.Handle(this, &ConfirmMemstickMoveScreen::OnMoveDataClick);
530
}
531
532
auto di = GetI18NCategory(I18NCat::DIALOG);
533
leftColumn->Add(new Choice(di->T("OK")))->OnClick.Handle(this, &ConfirmMemstickMoveScreen::OnConfirm);
534
leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
535
}
536
}
537
538
UI::EventReturn ConfirmMemstickMoveScreen::OnMoveDataClick(UI::EventParams &params) {
539
RecreateViews();
540
return UI::EVENT_DONE;
541
}
542
543
void ConfirmMemstickMoveScreen::update() {
544
UIDialogScreenWithBackground::update();
545
auto ms = GetI18NCategory(I18NCat::MEMSTICK);
546
547
if (moveDataTask_) {
548
if (progressView_) {
549
progressView_->SetText(progressReporter_.Format());
550
}
551
552
MoveResult *result = moveDataTask_->Poll();
553
554
if (result) {
555
if (result->success) {
556
progressReporter_.SetProgress(ms->T("Done!"));
557
INFO_LOG(Log::System, "Move data task finished successfully!");
558
// Succeeded!
559
FinishFolderMove();
560
} else {
561
progressReporter_.SetProgress(ms->T("Failed to move some files!"));
562
INFO_LOG(Log::System, "Move data task failed!");
563
// What do we do here? We might be in the middle of a move... Bad.
564
RecreateViews();
565
}
566
delete moveDataTask_;
567
moveDataTask_ = nullptr;
568
}
569
}
570
}
571
572
UI::EventReturn ConfirmMemstickMoveScreen::OnConfirm(UI::EventParams &params) {
573
// Transfer all the files in /PSP from the original directory.
574
// Should probably be done on a background thread so we can show some UI.
575
// So we probably need another screen for this with a progress bar..
576
// If the directory itself is called PSP, don't go below.
577
578
if (moveData_) {
579
progressReporter_.SetProgress(T(I18NCat::MEMSTICK, "Starting move..."));
580
581
moveDataTask_ = Promise<MoveResult *>::Spawn(&g_threadManager, [&]() -> MoveResult * {
582
Path moveSrc = g_Config.memStickDirectory;
583
Path moveDest = newMemstickFolder_;
584
return MoveDirectoryContentsSafe(moveSrc, moveDest, progressReporter_);
585
}, TaskType::IO_BLOCKING, TaskPriority::HIGH);
586
587
RecreateViews();
588
} else {
589
FinishFolderMove();
590
}
591
592
return UI::EVENT_DONE;
593
}
594
595
void ConfirmMemstickMoveScreen::FinishFolderMove() {
596
auto ms = GetI18NCategory(I18NCat::MEMSTICK);
597
598
Path oldMemstickFolder = g_Config.memStickDirectory;
599
600
// Successful so far, switch the memstick folder.
601
if (!SwitchMemstickFolderTo(newMemstickFolder_)) {
602
// TODO: More precise errors.
603
error_ = ms->T("That folder doesn't work as a memstick folder.");
604
return;
605
}
606
607
// If the chosen folder already had a config, reload it!
608
g_Config.Load();
609
610
// If the current browser directory is the old memstick folder, drop it.
611
if (g_Config.currentDirectory == oldMemstickFolder) {
612
g_Config.currentDirectory = g_Config.defaultCurrentDirectory;
613
}
614
615
PostLoadConfig();
616
617
if (!initialSetup_) {
618
// We restart the app here, to get the new settings.
619
System_RestartApp("");
620
} else {
621
// This is initial setup, we now switch to the main screen, if we were successful
622
// (which we better have been...)
623
if (g_Config.Save("MemstickPathChanged")) {
624
// TriggerFinish(DialogResult::DR_OK);
625
screenManager()->switchScreen(new MainScreen());
626
} else {
627
error_ = ms->T("Failed to save config");
628
RecreateViews();
629
}
630
}
631
}
632
633