2019-12-29 20:00:50 -05:00
|
|
|
package zenity
|
2019-12-13 08:45:23 -05:00
|
|
|
|
|
|
|
import (
|
2022-05-03 09:20:22 -04:00
|
|
|
"fmt"
|
2019-12-13 08:45:23 -05:00
|
|
|
"path/filepath"
|
2019-12-28 10:34:38 -05:00
|
|
|
"reflect"
|
2019-12-13 08:45:23 -05:00
|
|
|
"syscall"
|
|
|
|
"unicode/utf16"
|
|
|
|
"unsafe"
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
"github.com/ncruces/zenity/internal/win"
|
2022-05-19 18:39:21 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
_FOS_NOCHANGEDIR = 0x00000008
|
|
|
|
_FOS_PICKFOLDERS = 0x00000020
|
|
|
|
_FOS_FORCEFILESYSTEM = 0x00000040
|
|
|
|
_FOS_ALLOWMULTISELECT = 0x00000200
|
|
|
|
_FOS_FORCESHOWHIDDEN = 0x10000000
|
2019-12-13 20:07:25 -05:00
|
|
|
)
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-04 07:42:30 -05:00
|
|
|
func selectFile(opts options) (string, error) {
|
2020-01-09 20:46:53 -05:00
|
|
|
if opts.directory {
|
|
|
|
res, _, err := pickFolders(opts, false)
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
var args win.OPENFILENAME
|
2019-12-13 08:45:23 -05:00
|
|
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
2022-06-18 07:37:39 -04:00
|
|
|
args.Owner, _ = opts.attach.(win.HWND)
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags = win.OFN_NOCHANGEDIR | win.OFN_FILEMUSTEXIST | win.OFN_EXPLORER
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-03 22:25:45 -05:00
|
|
|
if opts.title != nil {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(*opts.title)
|
2019-12-13 08:45:23 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.showHidden {
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags |= win.OFN_FORCESHOWHIDDEN
|
2020-01-11 22:18:22 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.fileFilters != nil {
|
|
|
|
args.Filter = &initFilters(opts.fileFilters)[0]
|
2020-01-08 19:54:34 -05:00
|
|
|
}
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-04-27 20:27:28 -04:00
|
|
|
var res [32768]uint16
|
2019-12-13 08:45:23 -05:00
|
|
|
args.File = &res[0]
|
|
|
|
args.MaxFile = uint32(len(res))
|
2020-01-11 22:18:22 -05:00
|
|
|
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-26 08:40:24 -04:00
|
|
|
defer setup()()
|
2020-01-02 20:44:23 -05:00
|
|
|
|
2020-01-30 09:14:42 -05:00
|
|
|
if opts.ctx != nil {
|
2022-05-19 18:39:21 -04:00
|
|
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
2020-01-30 09:14:42 -05:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer unhook()
|
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
ok := win.GetOpenFileName(&args)
|
2020-01-30 09:14:42 -05:00
|
|
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
|
|
|
return "", opts.ctx.Err()
|
|
|
|
}
|
2022-06-17 22:45:49 -04:00
|
|
|
if !ok {
|
|
|
|
return "", win.CommDlgError()
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
2019-12-13 08:45:23 -05:00
|
|
|
return syscall.UTF16ToString(res[:]), nil
|
|
|
|
}
|
|
|
|
|
2022-05-19 09:09:21 -04:00
|
|
|
func selectFileMultiple(opts options) ([]string, error) {
|
2020-01-09 20:46:53 -05:00
|
|
|
if opts.directory {
|
|
|
|
_, res, err := pickFolders(opts, true)
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
var args win.OPENFILENAME
|
2019-12-13 08:45:23 -05:00
|
|
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
2022-06-18 07:37:39 -04:00
|
|
|
args.Owner, _ = opts.attach.(win.HWND)
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags = win.OFN_NOCHANGEDIR | win.OFN_ALLOWMULTISELECT | win.OFN_FILEMUSTEXIST | win.OFN_EXPLORER
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-03 22:25:45 -05:00
|
|
|
if opts.title != nil {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(*opts.title)
|
2019-12-13 08:45:23 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.showHidden {
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags |= win.OFN_FORCESHOWHIDDEN
|
2020-01-11 22:18:22 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.fileFilters != nil {
|
|
|
|
args.Filter = &initFilters(opts.fileFilters)[0]
|
2020-01-08 19:54:34 -05:00
|
|
|
}
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-04-27 20:27:28 -04:00
|
|
|
var res [32768 + 1024*256]uint16
|
2019-12-13 08:45:23 -05:00
|
|
|
args.File = &res[0]
|
|
|
|
args.MaxFile = uint32(len(res))
|
2020-01-11 22:18:22 -05:00
|
|
|
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-26 08:40:24 -04:00
|
|
|
defer setup()()
|
2020-01-02 20:44:23 -05:00
|
|
|
|
2020-01-30 09:14:42 -05:00
|
|
|
if opts.ctx != nil {
|
2022-05-19 18:39:21 -04:00
|
|
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
2020-01-30 09:14:42 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer unhook()
|
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
ok := win.GetOpenFileName(&args)
|
2020-01-30 09:14:42 -05:00
|
|
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
|
|
|
return nil, opts.ctx.Err()
|
|
|
|
}
|
2022-06-17 22:45:49 -04:00
|
|
|
if !ok {
|
|
|
|
return nil, win.CommDlgError()
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
2019-12-13 08:45:23 -05:00
|
|
|
|
|
|
|
var i int
|
|
|
|
var nul bool
|
|
|
|
var split []string
|
|
|
|
for j, p := range res {
|
|
|
|
if p == 0 {
|
|
|
|
if nul {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if i < j {
|
|
|
|
split = append(split, string(utf16.Decode(res[i:j])))
|
|
|
|
}
|
|
|
|
i = j + 1
|
|
|
|
nul = true
|
|
|
|
} else {
|
|
|
|
nul = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len := len(split) - 1; len > 0 {
|
|
|
|
base := split[0]
|
|
|
|
for i := 0; i < len; i++ {
|
|
|
|
split[i] = filepath.Join(base, string(split[i+1]))
|
|
|
|
}
|
|
|
|
split = split[:len]
|
|
|
|
}
|
|
|
|
return split, nil
|
|
|
|
}
|
|
|
|
|
2021-03-04 07:42:30 -05:00
|
|
|
func selectFileSave(opts options) (string, error) {
|
2020-01-11 22:18:22 -05:00
|
|
|
if opts.directory {
|
|
|
|
res, _, err := pickFolders(opts, false)
|
|
|
|
return res, err
|
|
|
|
}
|
2020-01-09 20:46:53 -05:00
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
var args win.OPENFILENAME
|
2019-12-13 08:45:23 -05:00
|
|
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
2022-06-18 07:37:39 -04:00
|
|
|
args.Owner, _ = opts.attach.(win.HWND)
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags = win.OFN_NOCHANGEDIR | win.OFN_PATHMUSTEXIST | win.OFN_NOREADONLYRETURN | win.OFN_EXPLORER
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-03 22:25:45 -05:00
|
|
|
if opts.title != nil {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(*opts.title)
|
2019-12-13 08:45:23 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.confirmOverwrite {
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags |= win.OFN_OVERWRITEPROMPT
|
2019-12-13 08:45:23 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.confirmCreate {
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags |= win.OFN_CREATEPROMPT
|
2020-01-11 22:18:22 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.showHidden {
|
2022-06-17 22:45:49 -04:00
|
|
|
args.Flags |= win.OFN_FORCESHOWHIDDEN
|
2020-01-11 22:18:22 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.fileFilters != nil {
|
|
|
|
args.Filter = &initFilters(opts.fileFilters)[0]
|
2020-01-08 19:54:34 -05:00
|
|
|
}
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-04-27 20:27:28 -04:00
|
|
|
var res [32768]uint16
|
2019-12-13 08:45:23 -05:00
|
|
|
args.File = &res[0]
|
|
|
|
args.MaxFile = uint32(len(res))
|
2020-01-11 22:18:22 -05:00
|
|
|
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2021-03-26 08:40:24 -04:00
|
|
|
defer setup()()
|
2020-01-02 20:44:23 -05:00
|
|
|
|
2020-01-30 09:14:42 -05:00
|
|
|
if opts.ctx != nil {
|
2022-05-19 18:39:21 -04:00
|
|
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
2020-01-30 09:14:42 -05:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer unhook()
|
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
ok := win.GetSaveFileName(&args)
|
2020-01-30 09:14:42 -05:00
|
|
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
|
|
|
return "", opts.ctx.Err()
|
|
|
|
}
|
2022-06-17 22:45:49 -04:00
|
|
|
if !ok {
|
|
|
|
return "", win.CommDlgError()
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
2019-12-13 08:45:23 -05:00
|
|
|
return syscall.UTF16ToString(res[:]), nil
|
|
|
|
}
|
|
|
|
|
2020-01-09 20:46:53 -05:00
|
|
|
func pickFolders(opts options, multi bool) (str string, lst []string, err error) {
|
2021-03-26 08:40:24 -04:00
|
|
|
defer setup()()
|
2020-01-02 20:44:23 -05:00
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
err = win.CoInitializeEx(0, 0x6) // COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE
|
|
|
|
if err != win.RPC_E_CHANGED_MODE {
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
2020-01-02 20:44:23 -05:00
|
|
|
}
|
2022-06-17 22:45:49 -04:00
|
|
|
defer win.CoUninitialize()
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
2020-01-02 20:44:23 -05:00
|
|
|
|
2019-12-28 10:34:38 -05:00
|
|
|
var dialog *_IFileOpenDialog
|
2022-06-17 22:45:49 -04:00
|
|
|
err = win.CoCreateInstance(
|
|
|
|
_CLSID_FileOpenDialog, nil, 0x17, // CLSCTX_ALL
|
|
|
|
_IID_IFileOpenDialog, unsafe.Pointer(&dialog))
|
|
|
|
if err != nil {
|
2021-07-12 20:43:52 -04:00
|
|
|
if multi {
|
2022-05-03 09:20:22 -04:00
|
|
|
return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
|
2021-07-12 20:43:52 -04:00
|
|
|
}
|
2020-01-12 13:55:10 -05:00
|
|
|
return browseForFolder(opts)
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
2021-03-10 09:49:09 -05:00
|
|
|
defer dialog.Call(dialog.Release)
|
2019-12-28 10:34:38 -05:00
|
|
|
|
2020-01-02 20:44:23 -05:00
|
|
|
var flgs int
|
2022-06-17 22:45:49 -04:00
|
|
|
hr, _, _ := dialog.Call(dialog.GetOptions, uintptr(unsafe.Pointer(&flgs)))
|
2020-01-02 20:44:23 -05:00
|
|
|
if int32(hr) < 0 {
|
2020-01-09 20:46:53 -05:00
|
|
|
return "", nil, syscall.Errno(hr)
|
|
|
|
}
|
2022-05-19 18:39:21 -04:00
|
|
|
flgs |= _FOS_NOCHANGEDIR | _FOS_PICKFOLDERS | _FOS_FORCEFILESYSTEM
|
2020-01-09 20:46:53 -05:00
|
|
|
if multi {
|
2022-05-19 18:39:21 -04:00
|
|
|
flgs |= _FOS_ALLOWMULTISELECT
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
2020-01-24 07:52:45 -05:00
|
|
|
if opts.showHidden {
|
2022-05-19 18:39:21 -04:00
|
|
|
flgs |= _FOS_FORCESHOWHIDDEN
|
2020-01-11 22:18:22 -05:00
|
|
|
}
|
2022-05-19 18:39:21 -04:00
|
|
|
hr, _, _ = dialog.Call(dialog.SetOptions, uintptr(flgs))
|
2020-01-02 20:44:23 -05:00
|
|
|
if int32(hr) < 0 {
|
2020-01-09 20:46:53 -05:00
|
|
|
return "", nil, syscall.Errno(hr)
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
2021-03-03 22:25:45 -05:00
|
|
|
if opts.title != nil {
|
|
|
|
ptr := syscall.StringToUTF16Ptr(*opts.title)
|
2021-03-10 09:49:09 -05:00
|
|
|
dialog.Call(dialog.SetTitle, uintptr(unsafe.Pointer(ptr)))
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
2020-01-02 20:44:23 -05:00
|
|
|
if opts.filename != "" {
|
2022-06-17 22:45:49 -04:00
|
|
|
var item *win.IShellItem
|
2020-01-02 20:44:23 -05:00
|
|
|
ptr := syscall.StringToUTF16Ptr(opts.filename)
|
2022-06-17 22:45:49 -04:00
|
|
|
win.SHCreateItemFromParsingName(ptr, nil, _IID_IShellItem, &item)
|
2019-12-28 10:34:38 -05:00
|
|
|
|
2021-03-07 20:12:32 -05:00
|
|
|
if int32(hr) >= 0 && item != nil {
|
2021-03-10 09:49:09 -05:00
|
|
|
dialog.Call(dialog.SetFolder, uintptr(unsafe.Pointer(item)))
|
|
|
|
item.Call(item.Release)
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-30 20:31:54 -05:00
|
|
|
if opts.ctx != nil {
|
2022-05-19 18:39:21 -04:00
|
|
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
2020-01-30 20:31:54 -05:00
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
defer unhook()
|
|
|
|
}
|
|
|
|
|
2022-06-18 07:37:39 -04:00
|
|
|
owner, _ := opts.attach.(win.HWND)
|
|
|
|
hr, _, _ = dialog.Call(dialog.Show, uintptr(owner))
|
2020-01-30 20:31:54 -05:00
|
|
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
|
|
|
return "", nil, opts.ctx.Err()
|
|
|
|
}
|
2020-01-02 20:44:23 -05:00
|
|
|
if hr == 0x800704c7 { // ERROR_CANCELLED
|
2021-04-29 11:05:28 -04:00
|
|
|
return "", nil, ErrCanceled
|
2020-01-02 20:44:23 -05:00
|
|
|
}
|
|
|
|
if int32(hr) < 0 {
|
2020-01-09 20:46:53 -05:00
|
|
|
return "", nil, syscall.Errno(hr)
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
shellItemPath := func(obj *win.COMObject, trap uintptr, a ...uintptr) error {
|
|
|
|
var item *win.IShellItem
|
2020-01-09 20:46:53 -05:00
|
|
|
hr, _, _ := obj.Call(trap, append(a, uintptr(unsafe.Pointer(&item)))...)
|
|
|
|
if int32(hr) < 0 {
|
|
|
|
return syscall.Errno(hr)
|
|
|
|
}
|
2021-03-10 09:49:09 -05:00
|
|
|
defer item.Call(item.Release)
|
2019-12-28 10:34:38 -05:00
|
|
|
|
2020-01-09 20:46:53 -05:00
|
|
|
var ptr uintptr
|
2021-03-10 09:49:09 -05:00
|
|
|
hr, _, _ = item.Call(item.GetDisplayName,
|
2020-01-09 20:46:53 -05:00
|
|
|
0x80058000, // SIGDN_FILESYSPATH
|
|
|
|
uintptr(unsafe.Pointer(&ptr)))
|
|
|
|
if int32(hr) < 0 {
|
|
|
|
return syscall.Errno(hr)
|
|
|
|
}
|
2022-06-17 22:45:49 -04:00
|
|
|
defer win.CoTaskMemFree(ptr)
|
2020-01-09 20:46:53 -05:00
|
|
|
|
2021-01-05 10:21:18 -05:00
|
|
|
var res []uint16
|
|
|
|
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&res))
|
2022-06-17 22:45:49 -04:00
|
|
|
hdr.Data, hdr.Len, hdr.Cap = uintptr(ptr), 32768, 32768
|
2021-01-05 10:21:18 -05:00
|
|
|
str = syscall.UTF16ToString(res)
|
2020-01-09 20:46:53 -05:00
|
|
|
lst = append(lst, str)
|
|
|
|
return nil
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
2020-01-09 20:46:53 -05:00
|
|
|
if multi {
|
|
|
|
var items *_IShellItemArray
|
2021-03-10 09:49:09 -05:00
|
|
|
hr, _, _ = dialog.Call(dialog.GetResults, uintptr(unsafe.Pointer(&items)))
|
2020-01-09 20:46:53 -05:00
|
|
|
if int32(hr) < 0 {
|
|
|
|
return "", nil, syscall.Errno(hr)
|
|
|
|
}
|
2021-03-10 09:49:09 -05:00
|
|
|
defer items.Call(items.Release)
|
2020-01-09 20:46:53 -05:00
|
|
|
|
|
|
|
var count uint32
|
2021-03-10 09:49:09 -05:00
|
|
|
hr, _, _ = items.Call(items.GetCount, uintptr(unsafe.Pointer(&count)))
|
2020-01-09 20:46:53 -05:00
|
|
|
if int32(hr) < 0 {
|
|
|
|
return "", nil, syscall.Errno(hr)
|
|
|
|
}
|
2020-01-11 22:18:22 -05:00
|
|
|
for i := uintptr(0); i < uintptr(count) && err == nil; i++ {
|
2022-06-17 22:45:49 -04:00
|
|
|
err = shellItemPath(&items.COMObject, items.GetItemAt, i)
|
2020-01-09 20:46:53 -05:00
|
|
|
}
|
|
|
|
} else {
|
2022-06-17 22:45:49 -04:00
|
|
|
err = shellItemPath(&dialog.COMObject, dialog.GetResult)
|
2020-01-09 20:46:53 -05:00
|
|
|
}
|
|
|
|
return
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
2020-01-12 13:55:10 -05:00
|
|
|
func browseForFolder(opts options) (string, []string, error) {
|
2022-06-17 22:45:49 -04:00
|
|
|
var args win.BROWSEINFO
|
2022-06-18 07:37:39 -04:00
|
|
|
args.Owner, _ = opts.attach.(win.HWND)
|
2019-12-28 10:34:38 -05:00
|
|
|
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
|
2019-12-13 20:07:25 -05:00
|
|
|
|
2021-03-03 22:25:45 -05:00
|
|
|
if opts.title != nil {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(*opts.title)
|
2020-01-12 13:55:10 -05:00
|
|
|
}
|
|
|
|
if opts.filename != "" {
|
2021-09-15 08:54:35 -04:00
|
|
|
args.LParam = syscall.StringToUTF16Ptr(opts.filename)
|
2021-09-13 08:17:15 -04:00
|
|
|
args.CallbackFunc = syscall.NewCallback(browseForFolderCallback)
|
2019-12-13 20:07:25 -05:00
|
|
|
}
|
|
|
|
|
2020-01-30 20:31:54 -05:00
|
|
|
if opts.ctx != nil {
|
2022-05-19 18:39:21 -04:00
|
|
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
2020-01-30 20:31:54 -05:00
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
|
|
|
defer unhook()
|
|
|
|
}
|
|
|
|
|
2022-06-17 22:45:49 -04:00
|
|
|
ptr := win.SHBrowseForFolder(&args)
|
2020-01-30 20:31:54 -05:00
|
|
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
|
|
|
return "", nil, opts.ctx.Err()
|
|
|
|
}
|
2019-12-13 20:07:25 -05:00
|
|
|
if ptr == 0 {
|
2021-04-29 11:05:28 -04:00
|
|
|
return "", nil, ErrCanceled
|
2019-12-13 20:07:25 -05:00
|
|
|
}
|
2022-06-17 22:45:49 -04:00
|
|
|
defer win.CoTaskMemFree(ptr)
|
2019-12-13 20:07:25 -05:00
|
|
|
|
2021-04-27 20:27:28 -04:00
|
|
|
var res [32768]uint16
|
2022-06-17 22:45:49 -04:00
|
|
|
win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0)
|
2019-12-13 20:07:25 -05:00
|
|
|
|
2020-01-09 20:46:53 -05:00
|
|
|
str := syscall.UTF16ToString(res[:])
|
|
|
|
return str, []string{str}, nil
|
2019-12-13 08:45:23 -05:00
|
|
|
}
|
|
|
|
|
2022-06-18 19:48:38 -04:00
|
|
|
func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uintptr {
|
2021-09-13 08:17:15 -04:00
|
|
|
if msg == 1 { // BFFM_INITIALIZED
|
2022-06-18 19:48:38 -04:00
|
|
|
win.SendMessage(wnd, 1024+103 /* BFFM_SETSELECTIONW */, 1 /* TRUE */, data)
|
2021-09-13 08:17:15 -04:00
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2020-01-11 22:18:22 -05:00
|
|
|
func initDirNameExt(filename string, name []uint16) (dir *uint16, ext *uint16) {
|
2021-02-18 08:24:02 -05:00
|
|
|
d, n := splitDirAndName(filename)
|
|
|
|
e := filepath.Ext(n)
|
|
|
|
if n != "" {
|
2021-05-07 09:02:44 -04:00
|
|
|
copy(name, syscall.StringToUTF16(n))
|
2021-02-18 08:24:02 -05:00
|
|
|
}
|
|
|
|
if d != "" {
|
|
|
|
dir = syscall.StringToUTF16Ptr(d)
|
|
|
|
}
|
2021-02-22 14:04:55 -05:00
|
|
|
if len(e) > 1 {
|
2021-02-18 08:24:02 -05:00
|
|
|
ext = syscall.StringToUTF16Ptr(e[1:])
|
2020-01-09 12:05:43 -05:00
|
|
|
}
|
2020-01-11 22:18:22 -05:00
|
|
|
return
|
2020-01-09 12:05:43 -05:00
|
|
|
}
|
|
|
|
|
2021-04-13 08:28:26 -04:00
|
|
|
func initFilters(filters FileFilters) []uint16 {
|
|
|
|
filters.simplify()
|
2021-04-13 09:37:10 -04:00
|
|
|
filters.name()
|
2019-12-13 08:45:23 -05:00
|
|
|
var res []uint16
|
|
|
|
for _, f := range filters {
|
|
|
|
res = append(res, utf16.Encode([]rune(f.Name))...)
|
|
|
|
res = append(res, 0)
|
2020-01-08 19:54:34 -05:00
|
|
|
for _, p := range f.Patterns {
|
|
|
|
res = append(res, utf16.Encode([]rune(p))...)
|
2019-12-13 08:45:23 -05:00
|
|
|
res = append(res, uint16(';'))
|
|
|
|
}
|
|
|
|
res = append(res, 0)
|
|
|
|
}
|
|
|
|
if res != nil {
|
|
|
|
res = append(res, 0)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2021-03-10 09:49:09 -05:00
|
|
|
// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
|
2019-12-28 10:34:38 -05:00
|
|
|
|
|
|
|
var (
|
|
|
|
_IID_IShellItem = uuid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe")
|
|
|
|
_IID_IFileOpenDialog = uuid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60")
|
|
|
|
_CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
|
|
|
|
)
|
|
|
|
|
|
|
|
type _IFileOpenDialog struct {
|
2022-06-17 22:45:49 -04:00
|
|
|
win.COMObject
|
2021-03-10 09:49:09 -05:00
|
|
|
*_IFileOpenDialogVtbl
|
2019-12-28 10:34:38 -05:00
|
|
|
}
|
|
|
|
|
2020-01-09 20:46:53 -05:00
|
|
|
type _IShellItemArray struct {
|
2022-06-17 22:45:49 -04:00
|
|
|
win.COMObject
|
2021-03-10 09:49:09 -05:00
|
|
|
*_IShellItemArrayVtbl
|
2020-01-09 20:46:53 -05:00
|
|
|
}
|
|
|
|
|
2019-12-28 10:34:38 -05:00
|
|
|
type _IFileOpenDialogVtbl struct {
|
|
|
|
_IFileDialogVtbl
|
|
|
|
GetResults uintptr
|
|
|
|
GetSelectedItems uintptr
|
|
|
|
}
|
|
|
|
|
|
|
|
type _IFileDialogVtbl struct {
|
|
|
|
_IModalWindowVtbl
|
|
|
|
SetFileTypes uintptr
|
|
|
|
SetFileTypeIndex uintptr
|
|
|
|
GetFileTypeIndex uintptr
|
|
|
|
Advise uintptr
|
|
|
|
Unadvise uintptr
|
|
|
|
SetOptions uintptr
|
|
|
|
GetOptions uintptr
|
|
|
|
SetDefaultFolder uintptr
|
|
|
|
SetFolder uintptr
|
|
|
|
GetFolder uintptr
|
|
|
|
GetCurrentSelection uintptr
|
|
|
|
SetFileName uintptr
|
|
|
|
GetFileName uintptr
|
|
|
|
SetTitle uintptr
|
|
|
|
SetOkButtonLabel uintptr
|
|
|
|
SetFileNameLabel uintptr
|
|
|
|
GetResult uintptr
|
|
|
|
AddPlace uintptr
|
|
|
|
SetDefaultExtension uintptr
|
|
|
|
Close uintptr
|
|
|
|
SetClientGuid uintptr
|
|
|
|
ClearClientData uintptr
|
|
|
|
SetFilter uintptr
|
|
|
|
}
|
|
|
|
|
|
|
|
type _IModalWindowVtbl struct {
|
|
|
|
_IUnknownVtbl
|
|
|
|
Show uintptr
|
|
|
|
}
|
|
|
|
|
2020-01-09 20:46:53 -05:00
|
|
|
type _IShellItemArrayVtbl struct {
|
|
|
|
_IUnknownVtbl
|
|
|
|
BindToHandler uintptr
|
|
|
|
GetPropertyStore uintptr
|
|
|
|
GetPropertyDescriptionList uintptr
|
|
|
|
GetAttributes uintptr
|
|
|
|
GetCount uintptr
|
|
|
|
GetItemAt uintptr
|
|
|
|
EnumItems uintptr
|
|
|
|
}
|