From 72f0fafe46737239c28cc5e5d03911008a34f3bb Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Fri, 13 Dec 2019 13:45:23 +0000 Subject: [PATCH] File dialogs for windows. --- dialog/dialog_windows.go | 155 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 dialog/dialog_windows.go diff --git a/dialog/dialog_windows.go b/dialog/dialog_windows.go new file mode 100644 index 0000000..bb9984b --- /dev/null +++ b/dialog/dialog_windows.go @@ -0,0 +1,155 @@ +package dialog + +import ( + "path/filepath" + "syscall" + "unicode/utf16" + "unsafe" +) + +func OpenFile(title, defaultPath string, filters []FileFilter) (string, error) { + comdlg32 := syscall.NewLazyDLL("comdlg32.dll") + getOpenFileName := comdlg32.NewProc("GetOpenFileNameW") + + var args _OPENFILENAME + args.StructSize = uint32(unsafe.Sizeof(args)) + + 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) { + comdlg32 := syscall.NewLazyDLL("comdlg32.dll") + getOpenFileName := comdlg32.NewProc("GetOpenFileNameW") + + var args _OPENFILENAME + args.StructSize = uint32(unsafe.Sizeof(args)) + args.Flags = 0x00080200 // OFN_ALLOWMULTISELECT|OFN_EXPLORER + + 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) { + comdlg32 := syscall.NewLazyDLL("comdlg32.dll") + getSaveFileName := comdlg32.NewProc("GetSaveFileNameW") + + var args _OPENFILENAME + args.StructSize = uint32(unsafe.Sizeof(args)) + + 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) { + // TODO + return "", nil +} + +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 + PvReserved unsafe.Pointer + DwReserved uint32 + FlagsEx uint32 +}