2019-12-13 08:45:23 -05:00
|
|
|
package dialog
|
|
|
|
|
|
|
|
import (
|
|
|
|
"path/filepath"
|
|
|
|
"syscall"
|
|
|
|
"unicode/utf16"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
2019-12-13 20:07:25 -05:00
|
|
|
var (
|
|
|
|
ole32 = syscall.NewLazyDLL("ole32.dll")
|
|
|
|
shell32 = syscall.NewLazyDLL("shell32.dll")
|
|
|
|
comdlg32 = syscall.NewLazyDLL("comdlg32.dll")
|
|
|
|
|
|
|
|
coTaskMemFree = ole32.NewProc("CoTaskMemFree")
|
|
|
|
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
|
|
|
|
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
|
|
|
|
browseForFolder = shell32.NewProc("SHBrowseForFolderW")
|
|
|
|
getPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
|
|
|
|
)
|
2019-12-13 08:45:23 -05:00
|
|
|
|
2019-12-13 20:07:25 -05:00
|
|
|
func OpenFile(title, defaultPath string, filters []FileFilter) (string, error) {
|
2019-12-13 08:45:23 -05:00
|
|
|
var args _OPENFILENAME
|
|
|
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
2019-12-13 20:07:25 -05:00
|
|
|
args.Flags = 0x00080000 // OFN_EXPLORER
|
2019-12-13 08:45:23 -05:00
|
|
|
|
|
|
|
if title != "" {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(title)
|
|
|
|
}
|
|
|
|
if defaultPath != "" {
|
|
|
|
args.InitialDir = syscall.StringToUTF16Ptr(defaultPath)
|
|
|
|
}
|
|
|
|
args.Filter = &windowsFilters(filters)[0]
|
|
|
|
|
|
|
|
res := [1024]uint16{}
|
|
|
|
args.File = &res[0]
|
|
|
|
args.MaxFile = uint32(len(res))
|
|
|
|
|
|
|
|
_, _, _ = getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
|
|
|
return syscall.UTF16ToString(res[:]), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func OpenFiles(title, defaultPath string, filters []FileFilter) ([]string, error) {
|
|
|
|
var args _OPENFILENAME
|
|
|
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
2019-12-13 20:07:25 -05:00
|
|
|
args.Flags = 0x00080200 // OFN_EXPLORER|OFN_ALLOWMULTISELECT
|
2019-12-13 08:45:23 -05:00
|
|
|
|
|
|
|
if title != "" {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(title)
|
|
|
|
}
|
|
|
|
if defaultPath != "" {
|
|
|
|
args.InitialDir = syscall.StringToUTF16Ptr(defaultPath)
|
|
|
|
}
|
|
|
|
args.Filter = &windowsFilters(filters)[0]
|
|
|
|
|
|
|
|
res := [65536]uint16{}
|
|
|
|
args.File = &res[0]
|
|
|
|
args.MaxFile = uint32(len(res))
|
|
|
|
|
|
|
|
_, _, _ = getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
func SaveFile(title, defaultPath string, confirmOverwrite bool, filters []FileFilter) (string, error) {
|
|
|
|
var args _OPENFILENAME
|
|
|
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
2019-12-13 20:07:25 -05:00
|
|
|
args.Flags = 0x00080000 // OFN_EXPLORER
|
2019-12-13 08:45:23 -05:00
|
|
|
|
|
|
|
if title != "" {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(title)
|
|
|
|
}
|
|
|
|
if defaultPath != "" {
|
|
|
|
args.InitialDir = syscall.StringToUTF16Ptr(defaultPath)
|
|
|
|
}
|
|
|
|
if confirmOverwrite {
|
|
|
|
args.Flags |= 0x00000002 // OFN_OVERWRITEPROMPT
|
|
|
|
}
|
|
|
|
args.Filter = &windowsFilters(filters)[0]
|
|
|
|
|
|
|
|
res := [1024]uint16{}
|
|
|
|
args.File = &res[0]
|
|
|
|
args.MaxFile = uint32(len(res))
|
|
|
|
|
|
|
|
_, _, _ = getSaveFileName.Call(uintptr(unsafe.Pointer(&args)))
|
|
|
|
return syscall.UTF16ToString(res[:]), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func PickFolder(title, defaultPath string) (string, error) {
|
2019-12-13 20:07:25 -05:00
|
|
|
var args _BROWSEINFO
|
|
|
|
args.Flags = 0x00000051 // BIF_RETURNONLYFSDIRS|BIF_USENEWUI
|
|
|
|
|
|
|
|
if title != "" {
|
|
|
|
args.Title = syscall.StringToUTF16Ptr(title)
|
|
|
|
}
|
|
|
|
|
|
|
|
ptr, _, _ := browseForFolder.Call(uintptr(unsafe.Pointer(&args)))
|
|
|
|
if ptr == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
res := [1024]uint16{}
|
|
|
|
_, _, _ = getPathFromIDListEx.Call(ptr, uintptr(unsafe.Pointer(&res[0])), uintptr(len(res)), 0)
|
|
|
|
_, _, _ = coTaskMemFree.Call(ptr)
|
|
|
|
|
|
|
|
return syscall.UTF16ToString(res[:]), nil
|
2019-12-13 08:45:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func windowsFilters(filters []FileFilter) []uint16 {
|
|
|
|
var res []uint16
|
|
|
|
for _, f := range filters {
|
|
|
|
res = append(res, utf16.Encode([]rune(f.Name))...)
|
|
|
|
res = append(res, 0)
|
|
|
|
for _, e := range f.Exts {
|
|
|
|
res = append(res, uint16('*'))
|
|
|
|
res = append(res, utf16.Encode([]rune(e))...)
|
|
|
|
res = append(res, uint16(';'))
|
|
|
|
}
|
|
|
|
res = append(res, 0)
|
|
|
|
}
|
|
|
|
if res != nil {
|
|
|
|
res = append(res, 0)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
type _OPENFILENAME struct {
|
|
|
|
StructSize uint32
|
|
|
|
Owner uintptr
|
|
|
|
Instance uintptr
|
|
|
|
Filter *uint16
|
|
|
|
CustomFilter *uint16
|
|
|
|
MaxCustomFilter uint32
|
|
|
|
FilterIndex uint32
|
|
|
|
File *uint16
|
|
|
|
MaxFile uint32
|
|
|
|
FileTitle *uint16
|
|
|
|
MaxFileTitle uint32
|
|
|
|
InitialDir *uint16
|
|
|
|
Title *uint16
|
|
|
|
Flags uint32
|
|
|
|
FileOffset uint16
|
|
|
|
FileExtension uint16
|
|
|
|
DefExt *uint16
|
|
|
|
CustData uintptr
|
|
|
|
FnHook uintptr
|
|
|
|
TemplateName *uint16
|
2019-12-13 20:07:25 -05:00
|
|
|
PvReserved uintptr
|
2019-12-13 08:45:23 -05:00
|
|
|
DwReserved uint32
|
|
|
|
FlagsEx uint32
|
|
|
|
}
|
2019-12-13 20:07:25 -05:00
|
|
|
|
|
|
|
type _BROWSEINFO struct {
|
|
|
|
Owner uintptr
|
|
|
|
Root *uint16
|
|
|
|
DisplayName *uint16
|
|
|
|
Title *uint16
|
|
|
|
Flags uint32
|
|
|
|
CallbackFunc uintptr
|
|
|
|
LParam uintptr
|
|
|
|
Image int32
|
|
|
|
}
|