~whereswaldon/gio-x

3152c159f1272ca70e8d1a6ee19be5f428496651 — Lajos Koszti 2 months ago a77f90e
explorer: add multi-file import for Windows

Intorduce the ability to import multiple files at once on Windows.

The selected file paths are split by null characters and the directory
is appended to each filename. If any file fails to open, all opened
files are closed and the error is returned.

Signed-off-by: Lajos Koszti <ajnasz@ajnasz.hu>
1 files changed, 61 insertions(+), 6 deletions(-)

M explorer/explorer_windows.go
M explorer/explorer_windows.go => explorer/explorer_windows.go +61 -6
@@ 22,10 22,12 @@ var (
	_GetOpenFileName = _Dialog32.NewProc("GetOpenFileNameW")

	// https://docs.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew
	_FlagFileMustExist   = uint32(0x00001000)
	_FlagForceShowHidden = uint32(0x10000000)
	_FlagOverwritePrompt = uint32(0x00000002)
	_FlagDisableLinks    = uint32(0x00100000)
	_FlagFileMustExist    = uint32(0x00001000)
	_FlagForceShowHidden  = uint32(0x10000000)
	_FlagOverwritePrompt  = uint32(0x00000002)
	_FlagDisableLinks     = uint32(0x00100000)
	_FlagAllowMultiSelect = uint32(0x00000200)
	_FlagExplorer         = uint32(0x00080000)

	_FilePathLength       = uint32(65535)
	_OpenFileStructLength = uint32(unsafe.Sizeof(_OpenFileName{}))


@@ 118,8 120,61 @@ func (e *Explorer) importFile(extensions ...string) (io.ReadCloser, error) {
	return os.Open(path)
}

func (e *Explorer) importFiles(_ ...string) ([]io.ReadCloser, error) {
	return nil, ErrNotAvailable
func (e *Explorer) importFiles(extensions ...string) ([]io.ReadCloser, error) {
	pathUTF16 := make([]uint16, _FilePathLength)

	open := _OpenFileName{
		File:       &pathUTF16[0],
		MaxFile:    _FilePathLength,
		Filter:     buildFilter(extensions),
		Flags:      _FlagFileMustExist | _FlagForceShowHidden | _FlagDisableLinks | _FlagAllowMultiSelect | _FlagExplorer,
		StructSize: _OpenFileStructLength,
	}

	if r, _, _ := _GetOpenFileName.Call(uintptr(unsafe.Pointer(&open))); r == 0 {
		return nil, ErrUserDecline
	}

	// Split the pathUTF16 by null characters
	paths := make([]string, 0)
	currentPath := make([]uint16, 0)
	for _, char := range pathUTF16 {
		if char == 0 {
			if len(currentPath) > 0 {
				paths = append(paths, windows.UTF16ToString(currentPath))
				currentPath = currentPath[:0]
			}
		} else {
			currentPath = append(currentPath, char)
		}
	}

	// The first element is the directory, append it to each filename
	dir := paths[0]
	filePaths := make([]string, len(paths)-1)
	for i, file := range paths[1:] {
		filePaths[i] = filepath.Join(dir, file)
	}

	if len(filePaths) == 0 {
		return nil, ErrUserDecline
	}

	files := make([]io.ReadCloser, len(filePaths))
	for i, filePath := range filePaths {
		file, err := os.Open(filePath)
		if err != nil {
			for _, file := range files {
				if file != nil {
					file.Close()
				}
			}
			return nil, err
		}
		files[i] = file
	}

	return files, nil
}

func buildFilter(extensions []string) *uint16 {