Use IFileOpenDialog where available.

This commit is contained in:
Nuno Cruces 2024-02-07 10:50:58 +00:00
parent 3f5b602117
commit c56588920f
2 changed files with 65 additions and 49 deletions

View file

@ -13,9 +13,12 @@ import (
) )
func selectFile(opts options) (string, error) { func selectFile(opts options) (string, error) {
name, _, shown, err := fileOpenDialog(opts, false)
if shown || opts.ctx != nil && opts.ctx.Err() != nil {
return name, err
}
if opts.directory { if opts.directory {
res, _, err := pickFolders(opts, false) return browseForFolder(opts)
return res, err
} }
var args win.OPENFILENAME var args win.OPENFILENAME
@ -38,12 +41,6 @@ func selectFile(opts options) (string, error) {
args.MaxFile = uint32(len(res)) args.MaxFile = uint32(len(res))
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
uninit, err := coInitialize()
if err != nil {
return "", err
}
defer uninit()
defer setup(args.Owner)() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
@ -62,9 +59,12 @@ func selectFile(opts options) (string, error) {
} }
func selectFileMultiple(opts options) ([]string, error) { func selectFileMultiple(opts options) ([]string, error) {
_, list, shown, err := fileOpenDialog(opts, true)
if shown || opts.ctx != nil && opts.ctx.Err() != nil {
return list, err
}
if opts.directory { if opts.directory {
_, res, err := pickFolders(opts, true) return nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
return res, err
} }
var args win.OPENFILENAME var args win.OPENFILENAME
@ -87,12 +87,6 @@ func selectFileMultiple(opts options) ([]string, error) {
args.MaxFile = uint32(len(res)) args.MaxFile = uint32(len(res))
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
uninit, err := coInitialize()
if err != nil {
return nil, err
}
defer uninit()
defer setup(args.Owner)() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
@ -137,8 +131,7 @@ func selectFileMultiple(opts options) ([]string, error) {
func selectFileSave(opts options) (string, error) { func selectFileSave(opts options) (string, error) {
if opts.directory { if opts.directory {
res, _, err := pickFolders(opts, false) return selectFile(opts)
return res, err
} }
var args win.OPENFILENAME var args win.OPENFILENAME
@ -167,12 +160,6 @@ func selectFileSave(opts options) (string, error) {
args.MaxFile = uint32(len(res)) args.MaxFile = uint32(len(res))
args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:]) args.InitialDir, args.DefExt = initDirNameExt(opts.filename, res[:])
uninit, err := coInitialize()
if err != nil {
return "", err
}
defer uninit()
defer setup(args.Owner)() defer setup(args.Owner)()
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
@ -190,10 +177,10 @@ func selectFileSave(opts options) (string, error) {
return syscall.UTF16ToString(res[:]), nil return syscall.UTF16ToString(res[:]), nil
} }
func pickFolders(opts options, multi bool) (string, []string, error) { func fileOpenDialog(opts options, multi bool) (string, []string, bool, error) {
uninit, err := coInitialize() uninit, err := coInitialize()
if err != nil { if err != nil {
return "", nil, err return "", nil, false, err
} }
defer uninit() defer uninit()
@ -205,27 +192,27 @@ func pickFolders(opts options, multi bool) (string, []string, error) {
win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL, win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL,
win.IID_IFileOpenDialog, unsafe.Pointer(&dialog)) win.IID_IFileOpenDialog, unsafe.Pointer(&dialog))
if err != nil { if err != nil {
if multi { return "", nil, false, err
return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
}
return browseForFolder(opts)
} }
defer dialog.Release() defer dialog.Release()
flgs, err := dialog.GetOptions() flgs, err := dialog.GetOptions()
if err != nil { if err != nil {
return "", nil, err return "", nil, false, err
} }
flgs |= win.FOS_NOCHANGEDIR | win.FOS_PICKFOLDERS | win.FOS_FORCEFILESYSTEM flgs |= win.FOS_NOCHANGEDIR | win.FOS_FILEMUSTEXIST | win.FOS_FORCEFILESYSTEM
if multi { if multi {
flgs |= win.FOS_ALLOWMULTISELECT flgs |= win.FOS_ALLOWMULTISELECT
} }
if opts.directory {
flgs |= win.FOS_PICKFOLDERS
}
if opts.showHidden { if opts.showHidden {
flgs |= win.FOS_FORCESHOWHIDDEN flgs |= win.FOS_FORCESHOWHIDDEN
} }
err = dialog.SetOptions(flgs) err = dialog.SetOptions(flgs)
if err != nil { if err != nil {
return "", nil, err return "", nil, false, err
} }
if opts.title != nil { if opts.title != nil {
@ -234,7 +221,12 @@ func pickFolders(opts options, multi bool) (string, []string, error) {
if opts.filename != "" { if opts.filename != "" {
var item *win.IShellItem var item *win.IShellItem
win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item) dir, name, _ := splitDirAndName(opts.filename)
dialog.SetFileName(strptr(name))
if ext := filepath.Ext(name); len(ext) > 1 {
dialog.SetDefaultExtension(strptr(ext[1:]))
}
win.SHCreateItemFromParsingName(strptr(dir), nil, win.IID_IShellItem, &item)
if item != nil { if item != nil {
defer item.Release() defer item.Release()
dialog.SetFolder(item) dialog.SetFolder(item)
@ -243,48 +235,48 @@ func pickFolders(opts options, multi bool) (string, []string, error) {
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", nil, err return "", nil, false, err
} }
defer unhook() defer unhook()
err = dialog.Show(owner) err = dialog.Show(owner)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", nil, opts.ctx.Err() return "", nil, true, opts.ctx.Err()
} }
if err == win.E_CANCELED { if err == win.E_CANCELED {
return "", nil, ErrCanceled return "", nil, true, ErrCanceled
} }
if err != nil { if err != nil {
return "", nil, err return "", nil, true, err
} }
if multi { if multi {
items, err := dialog.GetResults() items, err := dialog.GetResults()
if err != nil { if err != nil {
return "", nil, err return "", nil, true, err
} }
defer items.Release() defer items.Release()
count, err := items.GetCount() count, err := items.GetCount()
if err != nil { if err != nil {
return "", nil, err return "", nil, true, err
} }
var lst []string var lst []string
for i := uint32(0); i < count && err == nil; i++ { for i := uint32(0); i < count && err == nil; i++ {
str, err := shellItemPath(items.GetItemAt(i)) str, err := shellItemPath(items.GetItemAt(i))
if err != nil { if err != nil {
return "", nil, err return "", nil, true, err
} }
lst = append(lst, str) lst = append(lst, str)
} }
return "", lst, nil return "", lst, true, nil
} else { } else {
str, err := shellItemPath(dialog.GetResult()) str, err := shellItemPath(dialog.GetResult())
if err != nil { if err != nil {
return "", nil, err return "", nil, true, err
} }
return str, nil, nil return str, nil, true, nil
} }
} }
@ -296,7 +288,13 @@ func shellItemPath(item *win.IShellItem, err error) (string, error) {
return item.GetDisplayName(win.SIGDN_FILESYSPATH) return item.GetDisplayName(win.SIGDN_FILESYSPATH)
} }
func browseForFolder(opts options) (string, []string, error) { func browseForFolder(opts options) (string, error) {
uninit, err := coInitialize()
if err != nil {
return "", err
}
defer uninit()
var args win.BROWSEINFO var args win.BROWSEINFO
args.Owner, _ = opts.attach.(win.HWND) args.Owner, _ = opts.attach.(win.HWND)
args.Flags = win.BIF_RETURNONLYFSDIRS args.Flags = win.BIF_RETURNONLYFSDIRS
@ -311,16 +309,16 @@ func browseForFolder(opts options) (string, []string, error) {
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil) unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
defer unhook() defer unhook()
ptr := win.SHBrowseForFolder(&args) ptr := win.SHBrowseForFolder(&args)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return "", nil, opts.ctx.Err() return "", opts.ctx.Err()
} }
if ptr == nil { if ptr == nil {
return "", nil, ErrCanceled return "", ErrCanceled
} }
defer win.CoTaskMemFree(unsafe.Pointer(ptr)) defer win.CoTaskMemFree(unsafe.Pointer(ptr))
@ -328,7 +326,7 @@ func browseForFolder(opts options) (string, []string, error) {
win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0) win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0)
str := syscall.UTF16ToString(res[:]) str := syscall.UTF16ToString(res[:])
return str, []string{str}, nil return str, nil
} }
func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uintptr { func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uintptr {

View file

@ -197,6 +197,15 @@ func (u *IFileDialog) SetFolder(item *IShellItem) (err error) {
return return
} }
func (u *IFileDialog) SetFileName(name *uint16) (err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.SetFileName, uintptr(unsafe.Pointer(name)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (u *IFileDialog) SetTitle(title *uint16) (err error) { func (u *IFileDialog) SetTitle(title *uint16) (err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.SetTitle, uintptr(unsafe.Pointer(title))) hr, _, _ := u.call(vtbl.SetTitle, uintptr(unsafe.Pointer(title)))
@ -206,6 +215,15 @@ func (u *IFileDialog) SetTitle(title *uint16) (err error) {
return return
} }
func (u *IFileDialog) SetDefaultExtension(extension *uint16) (err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.SetDefaultExtension, uintptr(unsafe.Pointer(extension)))
if hr != 0 {
err = syscall.Errno(hr)
}
return
}
func (u *IFileDialog) GetResult() (item *IShellItem, err error) { func (u *IFileDialog) GetResult() (item *IShellItem, err error) {
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u)) vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
hr, _, _ := u.call(vtbl.GetResult, uintptr(unsafe.Pointer(&item))) hr, _, _ := u.call(vtbl.GetResult, uintptr(unsafe.Pointer(&item)))