mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
Merge f4edc038f2 into fb2ac429de
This commit is contained in:
commit
6e9371a9c2
4 changed files with 251 additions and 1 deletions
|
|
@ -86,6 +86,18 @@ LibCreateNewFile (
|
|||
IN BOOLEAN CreateFile
|
||||
);
|
||||
|
||||
/**
|
||||
Navigate to the parent of the directory currently displayed.
|
||||
|
||||
@retval EFI_SUCCESS The parent location was displayed successfully.
|
||||
@retval other errors Error occurred when opening or parsing the parent.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
LibGoUpOneLevel (
|
||||
VOID
|
||||
);
|
||||
|
||||
/**
|
||||
This function allows a caller to extract the current configuration for one
|
||||
or more named elements from the target driver.
|
||||
|
|
@ -273,7 +285,12 @@ LibCallback (
|
|||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (QuestionId >= FILE_OPTION_OFFSET) {
|
||||
if (QuestionId == KEY_VALUE_UP_ONE_LEVEL) {
|
||||
Status = LibGoUpOneLevel ();
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
} else if (QuestionId >= FILE_OPTION_OFFSET) {
|
||||
LibGetDevicePath (QuestionId);
|
||||
Status = LibUpdateFileExplorer (QuestionId);
|
||||
if (EFI_ERROR (Status)) {
|
||||
|
|
@ -528,6 +545,55 @@ LibStrDuplicate (
|
|||
return Dest;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the parent directory of an absolute file path.
|
||||
|
||||
The parent of a top level path (e.g. "\" or "\Name") is the volume root "\".
|
||||
|
||||
@param Path The absolute path whose parent is requested.
|
||||
|
||||
@return A newly allocated string holding the parent path, or NULL on failure.
|
||||
Caller is responsible to free the returned string.
|
||||
|
||||
**/
|
||||
CHAR16 *
|
||||
LibGetParentPath (
|
||||
IN CHAR16 *Path
|
||||
)
|
||||
{
|
||||
CHAR16 *Parent;
|
||||
UINTN Index;
|
||||
UINTN LastSlash;
|
||||
|
||||
if (Path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LastSlash = 0;
|
||||
for (Index = 0; Path[Index] != L'\0'; Index++) {
|
||||
if (Path[Index] == L'\\') {
|
||||
LastSlash = Index;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Only a leading backslash was found (root or a top level entry): parent is "\".
|
||||
//
|
||||
if (LastSlash == 0) {
|
||||
return LibStrDuplicate (L"\\");
|
||||
}
|
||||
|
||||
Parent = AllocateZeroPool ((LastSlash + 1) * sizeof (CHAR16));
|
||||
if (Parent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CopyMem (Parent, Path, LastSlash * sizeof (CHAR16));
|
||||
Parent[LastSlash] = L'\0';
|
||||
|
||||
return Parent;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Function gets the file information from an open file descriptor, and stores it
|
||||
|
|
@ -782,6 +848,17 @@ LibFindFileSystem (
|
|||
NoSimpleFsHandles = 0;
|
||||
OptionNumber = 0;
|
||||
|
||||
//
|
||||
// Showing the volume list: no directory is active, so no "Up" entry is offered.
|
||||
//
|
||||
if (gFileExplorerPrivate.CurDirPath != NULL) {
|
||||
FreePool (gFileExplorerPrivate.CurDirPath);
|
||||
gFileExplorerPrivate.CurDirPath = NULL;
|
||||
}
|
||||
|
||||
gFileExplorerPrivate.CurDirHandle = NULL;
|
||||
gFileExplorerPrivate.CurDeviceHandle = NULL;
|
||||
|
||||
//
|
||||
// Locate Handles that support Simple File System protocol
|
||||
//
|
||||
|
|
@ -1155,6 +1232,18 @@ LibFindFiles (
|
|||
|
||||
OptionNumber = 0;
|
||||
|
||||
//
|
||||
// Remember the directory being displayed so a synthetic "Up one level" entry
|
||||
// can navigate back to its parent (or the volume list at a volume root).
|
||||
//
|
||||
if (gFileExplorerPrivate.CurDirPath != NULL) {
|
||||
FreePool (gFileExplorerPrivate.CurDirPath);
|
||||
}
|
||||
|
||||
gFileExplorerPrivate.CurDirPath = LibStrDuplicate (FileName);
|
||||
gFileExplorerPrivate.CurDirHandle = FileHandle;
|
||||
gFileExplorerPrivate.CurDeviceHandle = DeviceHandle;
|
||||
|
||||
DirBufferSize = sizeof (EFI_FILE_INFO) + 1024;
|
||||
DirInfo = AllocateZeroPool (DirBufferSize);
|
||||
if (DirInfo == NULL) {
|
||||
|
|
@ -1195,6 +1284,15 @@ LibFindFiles (
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((StrCmp (DirInfo->FileName, L".") == 0) || (StrCmp (DirInfo->FileName, L"..") == 0)) {
|
||||
//
|
||||
// Skip the on-disk "." and ".." entries (present on FAT sub-directories but
|
||||
// not on the volume root or on other file systems). Parent navigation is
|
||||
// provided uniformly by the synthetic "Up one level" entry instead.
|
||||
//
|
||||
continue;
|
||||
}
|
||||
|
||||
NewMenuEntry = LibCreateMenuEntry ();
|
||||
if (NULL == NewMenuEntry) {
|
||||
Status = EFI_OUT_OF_RESOURCES;
|
||||
|
|
@ -1329,6 +1427,22 @@ LibUpdateFileExplorePage (
|
|||
|
||||
mQuestionIdUpdate += QUESTION_ID_UPDATE_STEP;
|
||||
|
||||
//
|
||||
// When a directory is being displayed, offer a synthetic "Up one level" entry
|
||||
// so the user can return to the parent directory (or the volume list at a
|
||||
// volume root) without having to exit the file explorer entirely.
|
||||
//
|
||||
if (gFileExplorerPrivate.CurDirPath != NULL) {
|
||||
HiiCreateGotoOpCode (
|
||||
mLibStartOpCodeHandle,
|
||||
FORM_FILE_EXPLORER_ID,
|
||||
STRING_TOKEN (STR_UP_ONE_LEVEL),
|
||||
STRING_TOKEN (STR_UP_ONE_LEVEL_HELP),
|
||||
EFI_IFR_FLAG_CALLBACK,
|
||||
KEY_VALUE_UP_ONE_LEVEL
|
||||
);
|
||||
}
|
||||
|
||||
for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
|
||||
NewMenuEntry = LibGetMenuEntry (MenuOption, Index);
|
||||
NewFileContext = (FILE_CONTEXT *)NewMenuEntry->VariableContext;
|
||||
|
|
@ -1439,6 +1553,107 @@ LibUpdateFileExplorer (
|
|||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
Navigate to the parent of the directory currently displayed.
|
||||
|
||||
When the volume root is displayed, the volume (file system) list is shown
|
||||
instead. Does nothing when the volume list is already displayed.
|
||||
|
||||
@retval EFI_SUCCESS The parent location was displayed successfully.
|
||||
@retval other errors Error occurred when opening or parsing the parent.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
LibGoUpOneLevel (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
CHAR16 *ParentPath;
|
||||
EFI_FILE_HANDLE RootHandle;
|
||||
EFI_FILE_HANDLE ParentHandle;
|
||||
EFI_HANDLE DeviceHandle;
|
||||
|
||||
//
|
||||
// Already at the volume list: nothing above it.
|
||||
//
|
||||
if (gFileExplorerPrivate.CurDirPath == NULL) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// At a volume root: go back to the volume (file system) list.
|
||||
//
|
||||
if (StrCmp (gFileExplorerPrivate.CurDirPath, L"\\") == 0) {
|
||||
if (gFileExplorerPrivate.CurDirHandle != NULL) {
|
||||
gFileExplorerPrivate.CurDirHandle->Close (gFileExplorerPrivate.CurDirHandle);
|
||||
gFileExplorerPrivate.CurDirHandle = NULL;
|
||||
}
|
||||
|
||||
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
|
||||
Status = LibFindFileSystem ();
|
||||
if (!EFI_ERROR (Status)) {
|
||||
LibUpdateFileExplorePage ();
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// In a sub-directory: open and display its parent.
|
||||
//
|
||||
DeviceHandle = gFileExplorerPrivate.CurDeviceHandle;
|
||||
ParentPath = LibGetParentPath (gFileExplorerPrivate.CurDirPath);
|
||||
if (ParentPath == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
RootHandle = LibOpenRoot (DeviceHandle);
|
||||
if (RootHandle == NULL) {
|
||||
FreePool (ParentPath);
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (StrCmp (ParentPath, L"\\") == 0) {
|
||||
ParentHandle = RootHandle;
|
||||
} else {
|
||||
Status = RootHandle->Open (
|
||||
RootHandle,
|
||||
&ParentHandle,
|
||||
ParentPath,
|
||||
EFI_FILE_READ_ONLY,
|
||||
0
|
||||
);
|
||||
RootHandle->Close (RootHandle);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (ParentPath);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Release the directory being left before its handle reference is overwritten.
|
||||
//
|
||||
if (gFileExplorerPrivate.CurDirHandle != NULL) {
|
||||
gFileExplorerPrivate.CurDirHandle->Close (gFileExplorerPrivate.CurDirHandle);
|
||||
gFileExplorerPrivate.CurDirHandle = NULL;
|
||||
}
|
||||
|
||||
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
|
||||
|
||||
Status = LibFindFiles (ParentHandle, ParentPath, DeviceHandle);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
LibUpdateFileExplorePage ();
|
||||
} else {
|
||||
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
|
||||
ParentHandle->Close (ParentHandle);
|
||||
}
|
||||
|
||||
FreePool (ParentPath);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the device path info saved in the menu structure.
|
||||
|
||||
|
|
@ -1557,6 +1772,22 @@ Done:
|
|||
|
||||
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
|
||||
|
||||
//
|
||||
// Release the directory currently displayed. Its handle is shared by the (now
|
||||
// freed) child entries, which are non-root and therefore never close it.
|
||||
//
|
||||
if (gFileExplorerPrivate.CurDirHandle != NULL) {
|
||||
gFileExplorerPrivate.CurDirHandle->Close (gFileExplorerPrivate.CurDirHandle);
|
||||
gFileExplorerPrivate.CurDirHandle = NULL;
|
||||
}
|
||||
|
||||
if (gFileExplorerPrivate.CurDirPath != NULL) {
|
||||
FreePool (gFileExplorerPrivate.CurDirPath);
|
||||
gFileExplorerPrivate.CurDirPath = NULL;
|
||||
}
|
||||
|
||||
gFileExplorerPrivate.CurDeviceHandle = NULL;
|
||||
|
||||
if (FileName != NULL) {
|
||||
FreePool (FileName);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,15 @@ typedef struct {
|
|||
CHAR16 *FileType;
|
||||
CHOOSE_HANDLER ChooseHandler;
|
||||
EFI_DEVICE_PATH_PROTOCOL *RetDevicePath;
|
||||
|
||||
//
|
||||
// Location currently displayed, used to synthesize an "Up one level" entry.
|
||||
// When CurDirPath is NULL the volume (file system) list is being shown and no
|
||||
// "Up" entry is offered.
|
||||
//
|
||||
EFI_HANDLE CurDeviceHandle;
|
||||
EFI_FILE_HANDLE CurDirHandle;
|
||||
CHAR16 *CurDirPath;
|
||||
} FILE_EXPLORER_CALLBACK_DATA;
|
||||
|
||||
#define FILE_EXPLORER_PRIVATE_FROM_THIS(a) CR (a, FILE_EXPLORER_CALLBACK_DATA, FeConfigAccess, FILE_EXPLORER_CALLBACK_DATA_SIGNATURE)
|
||||
|
|
|
|||
|
|
@ -53,3 +53,7 @@
|
|||
#language fr-FR "Create Folder and Exit"
|
||||
#string STR_NO_CREATE_FOLDER_AND_EXIT #language en-US "Discard Create and Exit"
|
||||
#language fr-FR "Discard Create and Exit"
|
||||
#string STR_UP_ONE_LEVEL #language en-US "<.. Up one level>"
|
||||
#language fr-FR "<.. Up one level>"
|
||||
#string STR_UP_ONE_LEVEL_HELP #language en-US "Return to the parent directory, or to the volume list when at the root of a volume"
|
||||
#language fr-FR "Return to the parent directory, or to the volume list when at the root of a volume"
|
||||
|
|
|
|||
|
|
@ -22,5 +22,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
|
|||
#define NEW_FOLDER_NAME_ID 0x3001
|
||||
#define KEY_VALUE_CREATE_FOLDER_AND_EXIT 0x3002
|
||||
#define KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT 0x3003
|
||||
//
|
||||
// Reserved question id used by the synthetic "Up one level" entry. It must stay
|
||||
// below NEW_FILE_QUESTION_ID_BASE (0x5000) so the incrementing new-file/new-folder
|
||||
// and FILE_OPTION_OFFSET (0x8000) question ids can never collide with it.
|
||||
//
|
||||
#define KEY_VALUE_UP_ONE_LEVEL 0x4000
|
||||
|
||||
#define LABEL_END 0xffff
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue