File API, windows fixes.
This commit is contained in:
parent
6bd07cf824
commit
339101c9f4
6 changed files with 198 additions and 130 deletions
44
file.go
44
file.go
|
@ -1,6 +1,50 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
|
type opts struct {
|
||||||
|
title string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*opts)
|
||||||
|
|
||||||
|
func (o *opts) Title(title string) {
|
||||||
|
o.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileopts struct {
|
||||||
|
opts
|
||||||
|
filename string
|
||||||
|
overwrite bool
|
||||||
|
filters []FileFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileOption func(*fileopts)
|
||||||
|
|
||||||
|
func Filename(filename string) FileOption {
|
||||||
|
return func(o *fileopts) {
|
||||||
|
o.filename = filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfirmOverwrite(o *fileopts) {
|
||||||
|
o.overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
type FileFilter struct {
|
type FileFilter struct {
|
||||||
Name string
|
Name string
|
||||||
Exts []string
|
Exts []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileFilters []FileFilter
|
||||||
|
|
||||||
|
func (f FileFilters) New() FileOption {
|
||||||
|
return func(o *fileopts) {
|
||||||
|
o.filters = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileoptsParse(options []FileOption) (res fileopts) {
|
||||||
|
for _, o := range options {
|
||||||
|
o(&res)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -8,13 +8,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectFile(title, defaultPath string, filters []FileFilter) (string, error) {
|
func SelectFile(options ...FileOption) (string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
cmd.Stdin = scriptExpand(scriptData{
|
||||||
Operation: "chooseFile",
|
Operation: "chooseFile",
|
||||||
Title: title,
|
Prompt: opts.title,
|
||||||
DefaultPath: defaultPath,
|
Location: opts.filename,
|
||||||
Filter: appleFilters(filters),
|
Type: appleFilters(opts.filters),
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -26,14 +28,16 @@ func SelectFile(title, defaultPath string, filters []FileFilter) (string, error)
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileMutiple(title, defaultPath string, filters []FileFilter) ([]string, error) {
|
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
cmd.Stdin = scriptExpand(scriptData{
|
||||||
Operation: "chooseFile",
|
Operation: "chooseFile",
|
||||||
Multiple: true,
|
Multiple: true,
|
||||||
Title: title,
|
Prompt: opts.title,
|
||||||
DefaultPath: defaultPath,
|
Location: opts.filename,
|
||||||
Filter: appleFilters(filters),
|
Type: appleFilters(opts.filters),
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -48,12 +52,14 @@ func SelectFileMutiple(title, defaultPath string, filters []FileFilter) ([]strin
|
||||||
return strings.Split(string(out), "\x00"), nil
|
return strings.Split(string(out), "\x00"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileSave(title, defaultPath string, confirmOverwrite bool, filters []FileFilter) (string, error) {
|
func SelectFileSave(options ...FileOption) (string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
cmd.Stdin = scriptExpand(scriptData{
|
||||||
Operation: "chooseFileName",
|
Operation: "chooseFileName",
|
||||||
Title: title,
|
Prompt: opts.title,
|
||||||
DefaultPath: defaultPath,
|
Location: opts.filename,
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,12 +71,14 @@ func SelectFileSave(title, defaultPath string, confirmOverwrite bool, filters []
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectDirectory(title, defaultPath string) (string, error) {
|
func SelectDirectory(options ...FileOption) (string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
cmd.Stdin = scriptExpand(scriptData{
|
||||||
Operation: "chooseFolder",
|
Operation: "chooseFolder",
|
||||||
Title: title,
|
Prompt: opts.title,
|
||||||
DefaultPath: defaultPath,
|
Location: opts.filename,
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,9 +102,9 @@ func appleFilters(filters []FileFilter) []string {
|
||||||
|
|
||||||
type scriptData struct {
|
type scriptData struct {
|
||||||
Operation string
|
Operation string
|
||||||
Title string
|
Prompt string
|
||||||
DefaultPath string
|
Location string
|
||||||
Filter []string
|
Type []string
|
||||||
Multiple bool
|
Multiple bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,14 +126,14 @@ app.includeStandardAdditions = true;
|
||||||
app.activate();
|
app.activate();
|
||||||
|
|
||||||
var opts = {};
|
var opts = {};
|
||||||
opts.withPrompt = {{.Title}};
|
opts.withPrompt = {{.Prompt}};
|
||||||
opts.multipleSelectionsAllowed = {{.Multiple}};
|
opts.multipleSelectionsAllowed = {{.Multiple}};
|
||||||
|
|
||||||
{{if .DefaultPath}}
|
{{if .Location}}
|
||||||
opts.defaultLocation = {{.DefaultPath}};
|
opts.defaultLocation = {{.Location}};
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if .Filter}}
|
{{if .Type}}
|
||||||
opts.ofType = {{.Filter}};
|
opts.ofType = {{.Type}};
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
var res;
|
var res;
|
||||||
|
|
|
@ -5,15 +5,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectFile(title, defaultPath string, filters []FileFilter) (string, error) {
|
func SelectFile(options ...FileOption) (string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection"}
|
args := []string{"--file-selection"}
|
||||||
if title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+title)
|
args = append(args, "--title="+opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+defaultPath)
|
args = append(args, "--filename="+opts.filename)
|
||||||
}
|
}
|
||||||
args = append(args, zenityFilters(filters)...)
|
args = append(args, zenityFilters(opts.filters)...)
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||||
|
@ -28,15 +30,17 @@ func SelectFile(title, defaultPath string, filters []FileFilter) (string, error)
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileMutiple(title, defaultPath string, filters []FileFilter) ([]string, error) {
|
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection", "--multiple", "--separator=\x1e"}
|
args := []string{"--file-selection", "--multiple", "--separator=\x1e"}
|
||||||
if title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+title)
|
args = append(args, "--title="+opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+defaultPath)
|
args = append(args, "--filename="+opts.filename)
|
||||||
}
|
}
|
||||||
args = append(args, zenityFilters(filters)...)
|
args = append(args, zenityFilters(opts.filters)...)
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||||
|
@ -51,18 +55,20 @@ func SelectFileMutiple(title, defaultPath string, filters []FileFilter) ([]strin
|
||||||
return strings.Split(string(out), "\x1e"), nil
|
return strings.Split(string(out), "\x1e"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileSave(title, defaultPath string, confirmOverwrite bool, filters []FileFilter) (string, error) {
|
func SelectFileSave(options ...FileOption) (string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection", "--save"}
|
args := []string{"--file-selection", "--save"}
|
||||||
if title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+title)
|
args = append(args, "--title="+opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+defaultPath)
|
args = append(args, "--filename="+opts.filename)
|
||||||
}
|
}
|
||||||
if confirmOverwrite {
|
if opts.overwrite {
|
||||||
args = append(args, "--confirm-overwrite")
|
args = append(args, "--confirm-overwrite")
|
||||||
}
|
}
|
||||||
args = append(args, zenityFilters(filters)...)
|
args = append(args, zenityFilters(opts.filters)...)
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||||
|
@ -77,13 +83,15 @@ func SelectFileSave(title, defaultPath string, confirmOverwrite bool, filters []
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectDirectory(title, defaultPath string) (string, error) {
|
func SelectDirectory(options ...FileOption) (string, error) {
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection", "--directory"}
|
args := []string{"--file-selection", "--directory"}
|
||||||
if title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+title)
|
args = append(args, "--title="+opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+defaultPath)
|
args = append(args, "--filename="+opts.filename)
|
||||||
}
|
}
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
|
|
14
file_test.go
14
file_test.go
|
@ -5,11 +5,11 @@ import "testing"
|
||||||
const defaultPath = ""
|
const defaultPath = ""
|
||||||
|
|
||||||
func TestSelectFile(t *testing.T) {
|
func TestSelectFile(t *testing.T) {
|
||||||
res, err := SelectFile("", defaultPath, []FileFilter{
|
res, err := SelectFile(Filename(defaultPath), FileFilters{
|
||||||
{"Go files", []string{".go"}},
|
{"Go files", []string{".go"}},
|
||||||
{"Web files", []string{".html", ".js", ".css"}},
|
{"Web files", []string{".html", ".js", ".css"}},
|
||||||
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
||||||
})
|
}.New())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -19,11 +19,11 @@ func TestSelectFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectFileMutiple(t *testing.T) {
|
func TestSelectFileMutiple(t *testing.T) {
|
||||||
res, err := SelectFileMutiple("", defaultPath, []FileFilter{
|
res, err := SelectFileMutiple(Filename(defaultPath), FileFilters{
|
||||||
{"Go files", []string{".go"}},
|
{"Go files", []string{".go"}},
|
||||||
{"Web files", []string{".html", ".js", ".css"}},
|
{"Web files", []string{".html", ".js", ".css"}},
|
||||||
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
||||||
})
|
}.New())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -33,11 +33,11 @@ func TestSelectFileMutiple(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectFileSave(t *testing.T) {
|
func TestSelectFileSave(t *testing.T) {
|
||||||
res, err := SelectFileSave("", defaultPath, true, []FileFilter{
|
res, err := SelectFileSave(Filename(defaultPath), ConfirmOverwrite, FileFilters{
|
||||||
{"Go files", []string{".go"}},
|
{"Go files", []string{".go"}},
|
||||||
{"Web files", []string{".html", ".js", ".css"}},
|
{"Web files", []string{".html", ".js", ".css"}},
|
||||||
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
||||||
})
|
}.New())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
@ -47,7 +47,7 @@ func TestSelectFileSave(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectDirectory(t *testing.T) {
|
func TestSelectDirectory(t *testing.T) {
|
||||||
res, err := SelectDirectory("", defaultPath)
|
res, err := SelectDirectory(Filename(defaultPath))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
141
file_windows.go
141
file_windows.go
|
@ -1,10 +1,10 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -27,60 +27,58 @@ var (
|
||||||
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
|
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectFile(title, defaultPath string, filters []FileFilter) (string, error) {
|
func SelectFile(options ...FileOption) (string, error) {
|
||||||
var args _OPENFILENAME
|
var args _OPENFILENAME
|
||||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||||
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
||||||
|
|
||||||
if title != "" {
|
opts := fileoptsParse(options)
|
||||||
args.Title = syscall.StringToUTF16Ptr(title)
|
if opts.title != "" {
|
||||||
|
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args.InitialDir = syscall.StringToUTF16Ptr(defaultPath)
|
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
||||||
}
|
}
|
||||||
args.Filter = &windowsFilters(filters)[0]
|
args.Filter = &windowsFilters(opts.filters)[0]
|
||||||
|
|
||||||
res := [32768]uint16{}
|
res := [32768]uint16{}
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
args.MaxFile = uint32(len(res))
|
args.MaxFile = uint32(len(res))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
n, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
n, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
n, _, _ = commDlgExtendedError.Call()
|
return "", commDlgError()
|
||||||
if n == 0 {
|
|
||||||
return "", nil
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("Common Dialog error: %x", n)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return syscall.UTF16ToString(res[:]), nil
|
return syscall.UTF16ToString(res[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileMutiple(title, defaultPath string, filters []FileFilter) ([]string, error) {
|
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||||
var args _OPENFILENAME
|
var args _OPENFILENAME
|
||||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||||
args.Flags = 0x80208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_EXPLORER
|
args.Flags = 0x80208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_EXPLORER
|
||||||
|
|
||||||
if title != "" {
|
opts := fileoptsParse(options)
|
||||||
args.Title = syscall.StringToUTF16Ptr(title)
|
if opts.title != "" {
|
||||||
|
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args.InitialDir = syscall.StringToUTF16Ptr(defaultPath)
|
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
||||||
}
|
}
|
||||||
args.Filter = &windowsFilters(filters)[0]
|
args.Filter = &windowsFilters(opts.filters)[0]
|
||||||
|
|
||||||
res := [32768 + 1024*256]uint16{}
|
res := [32768 + 1024*256]uint16{}
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
args.MaxFile = uint32(len(res))
|
args.MaxFile = uint32(len(res))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
n, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
n, _, _ := getOpenFileName.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
n, _, _ = commDlgExtendedError.Call()
|
return nil, commDlgError()
|
||||||
if n == 0 {
|
|
||||||
return nil, nil
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("Common Dialog error: %x", n)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
|
@ -110,72 +108,78 @@ func SelectFileMutiple(title, defaultPath string, filters []FileFilter) ([]strin
|
||||||
return split, nil
|
return split, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileSave(title, defaultPath string, confirmOverwrite bool, filters []FileFilter) (string, error) {
|
func SelectFileSave(options ...FileOption) (string, error) {
|
||||||
var args _OPENFILENAME
|
var args _OPENFILENAME
|
||||||
args.StructSize = uint32(unsafe.Sizeof(args))
|
args.StructSize = uint32(unsafe.Sizeof(args))
|
||||||
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
args.Flags = 0x80008 // OFN_NOCHANGEDIR|OFN_EXPLORER
|
||||||
|
|
||||||
if title != "" {
|
opts := fileoptsParse(options)
|
||||||
args.Title = syscall.StringToUTF16Ptr(title)
|
if opts.title != "" {
|
||||||
|
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||||
}
|
}
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
args.InitialDir = syscall.StringToUTF16Ptr(defaultPath)
|
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
||||||
}
|
}
|
||||||
if confirmOverwrite {
|
if opts.overwrite {
|
||||||
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
|
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
|
||||||
}
|
}
|
||||||
args.Filter = &windowsFilters(filters)[0]
|
args.Filter = &windowsFilters(opts.filters)[0]
|
||||||
|
|
||||||
res := [32768]uint16{}
|
res := [32768]uint16{}
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
args.MaxFile = uint32(len(res))
|
args.MaxFile = uint32(len(res))
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
n, _, _ := getSaveFileName.Call(uintptr(unsafe.Pointer(&args)))
|
n, _, _ := getSaveFileName.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
n, _, _ = commDlgExtendedError.Call()
|
return "", commDlgError()
|
||||||
if n == 0 {
|
|
||||||
return "", nil
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("Common Dialog error: %x", n)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return syscall.UTF16ToString(res[:]), nil
|
return syscall.UTF16ToString(res[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectDirectory(title, defaultPath string) (string, error) {
|
func SelectDirectory(options ...FileOption) (string, error) {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
hr, _, _ := coInitializeEx.Call(0, 0x6) // COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE
|
hr, _, _ := coInitializeEx.Call(0, 0x6) // COINIT_APARTMENTTHREADED|COINIT_DISABLE_OLE1DDE
|
||||||
if hr < 0 {
|
if hr != 0x80010106 { // RPC_E_CHANGED_MODE
|
||||||
return "", errors.New("COM initialization failed.")
|
if int32(hr) < 0 {
|
||||||
|
return "", syscall.Errno(hr)
|
||||||
}
|
}
|
||||||
defer coUninitialize.Call()
|
defer coUninitialize.Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := fileoptsParse(options)
|
||||||
|
|
||||||
var dialog *_IFileOpenDialog
|
var dialog *_IFileOpenDialog
|
||||||
hr, _, _ = coCreateInstance.Call(
|
hr, _, _ = coCreateInstance.Call(
|
||||||
_CLSID_FileOpenDialog, 0, 0x17, // CLSCTX_ALL
|
_CLSID_FileOpenDialog, 0, 0x17, // CLSCTX_ALL
|
||||||
_IID_IFileOpenDialog, uintptr(unsafe.Pointer(&dialog)))
|
_IID_IFileOpenDialog, uintptr(unsafe.Pointer(&dialog)))
|
||||||
if hr < 0 || dialog == nil {
|
if int32(hr) < 0 {
|
||||||
return browseForFolder(title, defaultPath)
|
return browseForFolder(opts.title)
|
||||||
}
|
}
|
||||||
defer dialog.Call(dialog.vtbl.Release)
|
defer dialog.Call(dialog.vtbl.Release)
|
||||||
|
|
||||||
var opts int
|
var flgs int
|
||||||
hr, _, _ = dialog.Call(dialog.vtbl.GetOptions, uintptr(unsafe.Pointer(&opts)))
|
hr, _, _ = dialog.Call(dialog.vtbl.GetOptions, uintptr(unsafe.Pointer(&flgs)))
|
||||||
if hr < 0 {
|
if int32(hr) < 0 {
|
||||||
return "", fmt.Errorf("IFileOpenDialog.GetOptions error: %x", hr)
|
return "", syscall.Errno(hr)
|
||||||
}
|
}
|
||||||
hr, _, _ = dialog.Call(dialog.vtbl.SetOptions, uintptr(opts|0x68)) // FOS_NOCHANGEDIR|FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM
|
hr, _, _ = dialog.Call(dialog.vtbl.SetOptions, uintptr(flgs|0x68)) // FOS_NOCHANGEDIR|FOS_PICKFOLDERS|FOS_FORCEFILESYSTEM
|
||||||
if hr < 0 {
|
if int32(hr) < 0 {
|
||||||
return "", fmt.Errorf("IFileOpenDialog.SetOptions error: %x", hr)
|
return "", syscall.Errno(hr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if title != "" {
|
if opts.title != "" {
|
||||||
ptr := syscall.StringToUTF16Ptr(title)
|
ptr := syscall.StringToUTF16Ptr(opts.title)
|
||||||
dialog.Call(dialog.vtbl.SetTitle, uintptr(unsafe.Pointer(ptr)))
|
dialog.Call(dialog.vtbl.SetTitle, uintptr(unsafe.Pointer(ptr)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if defaultPath != "" {
|
if opts.filename != "" {
|
||||||
var item *_IShellItem
|
var item *_IShellItem
|
||||||
ptr := syscall.StringToUTF16Ptr(defaultPath)
|
ptr := syscall.StringToUTF16Ptr(opts.filename)
|
||||||
hr, _, _ = shCreateItemFromParsingName.Call(
|
hr, _, _ = shCreateItemFromParsingName.Call(
|
||||||
uintptr(unsafe.Pointer(ptr)), 0,
|
uintptr(unsafe.Pointer(ptr)), 0,
|
||||||
_IID_IShellItem,
|
_IID_IShellItem,
|
||||||
|
@ -188,17 +192,17 @@ func SelectDirectory(title, defaultPath string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hr, _, _ = dialog.Call(dialog.vtbl.Show, 0)
|
hr, _, _ = dialog.Call(dialog.vtbl.Show, 0)
|
||||||
if hr < 0 {
|
if hr == 0x800704c7 { // ERROR_CANCELLED
|
||||||
return "", fmt.Errorf("IFileOpenDialog.Show error: %x", hr)
|
return "", nil
|
||||||
|
}
|
||||||
|
if int32(hr) < 0 {
|
||||||
|
return "", syscall.Errno(hr)
|
||||||
}
|
}
|
||||||
|
|
||||||
var item *_IShellItem
|
var item *_IShellItem
|
||||||
hr, _, _ = dialog.Call(dialog.vtbl.GetResult, uintptr(unsafe.Pointer(&item)))
|
hr, _, _ = dialog.Call(dialog.vtbl.GetResult, uintptr(unsafe.Pointer(&item)))
|
||||||
if hr < 0 {
|
if int32(hr) < 0 {
|
||||||
return "", fmt.Errorf("IFileOpenDialog.GetResult error: %x", hr)
|
return "", syscall.Errno(hr)
|
||||||
}
|
|
||||||
if item == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
}
|
||||||
defer item.Call(item.vtbl.Release)
|
defer item.Call(item.vtbl.Release)
|
||||||
|
|
||||||
|
@ -206,8 +210,8 @@ func SelectDirectory(title, defaultPath string) (string, error) {
|
||||||
hr, _, _ = item.Call(item.vtbl.GetDisplayName,
|
hr, _, _ = item.Call(item.vtbl.GetDisplayName,
|
||||||
0x80058000, // SIGDN_FILESYSPATH
|
0x80058000, // SIGDN_FILESYSPATH
|
||||||
uintptr(unsafe.Pointer(&ptr)))
|
uintptr(unsafe.Pointer(&ptr)))
|
||||||
if hr < 0 {
|
if int32(hr) < 0 {
|
||||||
return "", fmt.Errorf("IShellItem.GetDisplayName error: %x", hr)
|
return "", syscall.Errno(hr)
|
||||||
}
|
}
|
||||||
defer coTaskMemFree.Call(ptr)
|
defer coTaskMemFree.Call(ptr)
|
||||||
|
|
||||||
|
@ -215,7 +219,7 @@ func SelectDirectory(title, defaultPath string) (string, error) {
|
||||||
return syscall.UTF16ToString(*(*[]uint16)(unsafe.Pointer(&res))), nil
|
return syscall.UTF16ToString(*(*[]uint16)(unsafe.Pointer(&res))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func browseForFolder(title, defaultPath string) (string, error) {
|
func browseForFolder(title string) (string, error) {
|
||||||
var args _BROWSEINFO
|
var args _BROWSEINFO
|
||||||
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
|
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
|
||||||
|
|
||||||
|
@ -253,6 +257,15 @@ func windowsFilters(filters []FileFilter) []uint16 {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func commDlgError() error {
|
||||||
|
n, _, _ := commDlgExtendedError.Call()
|
||||||
|
if n == 0 {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Common Dialog error: %x", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type _OPENFILENAME struct {
|
type _OPENFILENAME struct {
|
||||||
StructSize uint32
|
StructSize uint32
|
||||||
Owner uintptr
|
Owner uintptr
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
user32 := syscall.NewLazyDLL("user32.dll")
|
|
||||||
user32.NewProc("SetProcessDPIAware").Call()
|
user32.NewProc("SetProcessDPIAware").Call()
|
||||||
}
|
}
|
Loading…
Reference in a new issue