Message dialogs, macos improvements.
This commit is contained in:
parent
339101c9f4
commit
9b63531d6a
14 changed files with 600 additions and 146 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -13,3 +13,5 @@
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
|
osa_gen_darwin.go
|
50
file.go
50
file.go
|
@ -1,50 +0,0 @@
|
||||||
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 {
|
|
||||||
Name 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
|
|
||||||
}
|
|
|
@ -1,24 +1,17 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"html/template"
|
|
||||||
"io"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectFile(options ...FileOption) (string, error) {
|
func SelectFile(options ...Option) (string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
out, err := osaRun("file", osaFile{
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
|
||||||
Operation: "chooseFile",
|
Operation: "chooseFile",
|
||||||
Prompt: opts.title,
|
Prompt: opts.title,
|
||||||
Location: opts.filename,
|
Location: opts.filename,
|
||||||
Type: appleFilters(opts.filters),
|
Type: appleFilters(opts.filters),
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -28,18 +21,15 @@ func SelectFile(options ...FileOption) (string, error) {
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
out, err := osaRun("file", osaFile{
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
|
||||||
Operation: "chooseFile",
|
Operation: "chooseFile",
|
||||||
Multiple: true,
|
Multiple: true,
|
||||||
Prompt: opts.title,
|
Prompt: opts.title,
|
||||||
Location: opts.filename,
|
Location: opts.filename,
|
||||||
Type: appleFilters(opts.filters),
|
Type: appleFilters(opts.filters),
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -52,16 +42,13 @@ func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||||
return strings.Split(string(out), "\x00"), nil
|
return strings.Split(string(out), "\x00"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileSave(options ...FileOption) (string, error) {
|
func SelectFileSave(options ...Option) (string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
out, err := osaRun("file", osaFile{
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
|
||||||
Operation: "chooseFileName",
|
Operation: "chooseFileName",
|
||||||
Prompt: opts.title,
|
Prompt: opts.title,
|
||||||
Location: opts.filename,
|
Location: opts.filename,
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -71,16 +58,13 @@ func SelectFileSave(options ...FileOption) (string, error) {
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectDirectory(options ...FileOption) (string, error) {
|
func SelectDirectory(options ...Option) (string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
out, err := osaRun("file", osaFile{
|
||||||
cmd := exec.Command("osascript", "-l", "JavaScript")
|
|
||||||
cmd.Stdin = scriptExpand(scriptData{
|
|
||||||
Operation: "chooseFolder",
|
Operation: "chooseFolder",
|
||||||
Prompt: opts.title,
|
Prompt: opts.title,
|
||||||
Location: opts.filename,
|
Location: opts.filename,
|
||||||
})
|
})
|
||||||
out, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -100,53 +84,10 @@ func appleFilters(filters []FileFilter) []string {
|
||||||
return filter
|
return filter
|
||||||
}
|
}
|
||||||
|
|
||||||
type scriptData struct {
|
type osaFile struct {
|
||||||
Operation string
|
Operation string
|
||||||
Prompt string
|
Prompt string
|
||||||
Location string
|
Location string
|
||||||
Type []string
|
Type []string
|
||||||
Multiple bool
|
Multiple bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func scriptExpand(data scriptData) io.Reader {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
err := script.Execute(&buf, data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var slice = buf.Bytes()
|
|
||||||
return bytes.NewReader(slice[len("<script>") : len(slice)-len("</script>")])
|
|
||||||
}
|
|
||||||
|
|
||||||
var script = template.Must(template.New("").Parse(`<script>
|
|
||||||
var app = Application.currentApplication();
|
|
||||||
app.includeStandardAdditions = true;
|
|
||||||
app.activate();
|
|
||||||
|
|
||||||
var opts = {};
|
|
||||||
opts.withPrompt = {{.Prompt}};
|
|
||||||
opts.multipleSelectionsAllowed = {{.Multiple}};
|
|
||||||
|
|
||||||
{{if .Location}}
|
|
||||||
opts.defaultLocation = {{.Location}};
|
|
||||||
{{end}}
|
|
||||||
{{if .Type}}
|
|
||||||
opts.ofType = {{.Type}};
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
var res;
|
|
||||||
try {
|
|
||||||
res = app[{{.Operation}}](opts);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.errorNumber !== -128) throw e;
|
|
||||||
}
|
|
||||||
if (Array.isArray(res)) {
|
|
||||||
res.join('\0');
|
|
||||||
} else if (res != null) {
|
|
||||||
res.toString();
|
|
||||||
} else {
|
|
||||||
void 0;
|
|
||||||
}
|
|
||||||
</script>`))
|
|
||||||
|
|
|
@ -5,15 +5,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectFile(options ...FileOption) (string, error) {
|
func SelectFile(options ...Option) (string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection"}
|
args := []string{"--file-selection"}
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+opts.title)
|
args = append(args, "--title", opts.title)
|
||||||
}
|
}
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+opts.filename)
|
args = append(args, "--filename", opts.filename)
|
||||||
}
|
}
|
||||||
args = append(args, zenityFilters(opts.filters)...)
|
args = append(args, zenityFilters(opts.filters)...)
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
|
@ -30,15 +30,15 @@ func SelectFile(options ...FileOption) (string, error) {
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection", "--multiple", "--separator=\x1e"}
|
args := []string{"--file-selection", "--multiple", "--separator=\x1e"}
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+opts.title)
|
args = append(args, "--title", opts.title)
|
||||||
}
|
}
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+opts.filename)
|
args = append(args, "--filename", opts.filename)
|
||||||
}
|
}
|
||||||
args = append(args, zenityFilters(opts.filters)...)
|
args = append(args, zenityFilters(opts.filters)...)
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
|
@ -55,15 +55,15 @@ func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||||
return strings.Split(string(out), "\x1e"), nil
|
return strings.Split(string(out), "\x1e"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileSave(options ...FileOption) (string, error) {
|
func SelectFileSave(options ...Option) (string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection", "--save"}
|
args := []string{"--file-selection", "--save"}
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+opts.title)
|
args = append(args, "--title", opts.title)
|
||||||
}
|
}
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+opts.filename)
|
args = append(args, "--filename", opts.filename)
|
||||||
}
|
}
|
||||||
if opts.overwrite {
|
if opts.overwrite {
|
||||||
args = append(args, "--confirm-overwrite")
|
args = append(args, "--confirm-overwrite")
|
||||||
|
@ -83,15 +83,15 @@ func SelectFileSave(options ...FileOption) (string, error) {
|
||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectDirectory(options ...FileOption) (string, error) {
|
func SelectDirectory(options ...Option) (string, error) {
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
|
||||||
args := []string{"--file-selection", "--directory"}
|
args := []string{"--file-selection", "--directory"}
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args = append(args, "--title="+opts.title)
|
args = append(args, "--title", opts.title)
|
||||||
}
|
}
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
args = append(args, "--filename="+opts.filename)
|
args = append(args, "--filename", opts.filename)
|
||||||
}
|
}
|
||||||
cmd := exec.Command("zenity", args...)
|
cmd := exec.Command("zenity", args...)
|
||||||
out, err := cmd.Output()
|
out, err := cmd.Output()
|
||||||
|
|
|
@ -27,12 +27,12 @@ var (
|
||||||
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
|
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
|
||||||
)
|
)
|
||||||
|
|
||||||
func SelectFile(options ...FileOption) (string, error) {
|
func SelectFile(options ...Option) (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
|
||||||
|
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||||
}
|
}
|
||||||
|
@ -55,12 +55,12 @@ func SelectFile(options ...FileOption) (string, error) {
|
||||||
return syscall.UTF16ToString(res[:]), nil
|
return syscall.UTF16ToString(res[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
func SelectFileMutiple(options ...Option) ([]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
|
||||||
|
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||||
}
|
}
|
||||||
|
@ -108,12 +108,12 @@ func SelectFileMutiple(options ...FileOption) ([]string, error) {
|
||||||
return split, nil
|
return split, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectFileSave(options ...FileOption) (string, error) {
|
func SelectFileSave(options ...Option) (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
|
||||||
|
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
if opts.title != "" {
|
if opts.title != "" {
|
||||||
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
args.Title = syscall.StringToUTF16Ptr(opts.title)
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ func SelectFileSave(options ...FileOption) (string, error) {
|
||||||
return syscall.UTF16ToString(res[:]), nil
|
return syscall.UTF16ToString(res[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectDirectory(options ...FileOption) (string, error) {
|
func SelectDirectory(options ...Option) (string, error) {
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ func SelectDirectory(options ...FileOption) (string, error) {
|
||||||
defer coUninitialize.Call()
|
defer coUninitialize.Call()
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := fileoptsParse(options)
|
opts := optsParse(options)
|
||||||
|
|
||||||
var dialog *_IFileOpenDialog
|
var dialog *_IFileOpenDialog
|
||||||
hr, _, _ = coCreateInstance.Call(
|
hr, _, _ = coCreateInstance.Call(
|
||||||
|
|
119
msg_darwin.go
Normal file
119
msg_darwin.go
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Error(text string, options ...Option) (bool, error) {
|
||||||
|
return message(0, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(text string, options ...Option) (bool, error) {
|
||||||
|
return message(1, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Question(text string, options ...Option) (bool, error) {
|
||||||
|
return message(2, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warning(text string, options ...Option) (bool, error) {
|
||||||
|
return message(3, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func message(dialog int, text string, options []Option) (bool, error) {
|
||||||
|
opts := optsParse(options)
|
||||||
|
|
||||||
|
data := osaMsg{
|
||||||
|
Text: text,
|
||||||
|
Title: opts.title,
|
||||||
|
Dialog: opts.icon != 0 || dialog == 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.Dialog {
|
||||||
|
switch opts.icon {
|
||||||
|
case ErrorIcon:
|
||||||
|
data.Icon = "stop"
|
||||||
|
case InfoIcon, QuestionIcon:
|
||||||
|
data.Icon = "note"
|
||||||
|
case WarningIcon:
|
||||||
|
data.Icon = "caution"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch dialog {
|
||||||
|
case 0:
|
||||||
|
data.As = "critical"
|
||||||
|
case 1:
|
||||||
|
data.As = "informational"
|
||||||
|
case 3:
|
||||||
|
data.As = "warning"
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.title != "" {
|
||||||
|
data.Text = opts.title
|
||||||
|
data.Message = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dialog != 2 {
|
||||||
|
opts.cancel = ""
|
||||||
|
if data.Dialog {
|
||||||
|
opts.ok = "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.ok != "" || opts.cancel != "" || opts.extra != "" || true {
|
||||||
|
if opts.ok == "" {
|
||||||
|
opts.ok = "OK"
|
||||||
|
}
|
||||||
|
if opts.cancel == "" {
|
||||||
|
opts.cancel = "Cancel"
|
||||||
|
}
|
||||||
|
if dialog == 2 {
|
||||||
|
if opts.extra == "" {
|
||||||
|
data.Buttons = []string{opts.cancel, opts.ok}
|
||||||
|
data.Default = 2
|
||||||
|
data.Cancel = 1
|
||||||
|
} else {
|
||||||
|
data.Buttons = []string{opts.extra, opts.cancel, opts.ok}
|
||||||
|
data.Default = 3
|
||||||
|
data.Cancel = 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if opts.extra == "" {
|
||||||
|
data.Buttons = []string{opts.ok}
|
||||||
|
data.Default = 1
|
||||||
|
} else {
|
||||||
|
data.Buttons = []string{opts.extra, opts.ok}
|
||||||
|
data.Default = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.defcancel {
|
||||||
|
if data.Cancel != 0 {
|
||||||
|
data.Default = data.Cancel
|
||||||
|
}
|
||||||
|
if data.Dialog && data.Buttons == nil {
|
||||||
|
data.Default = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := osaRun("msg", data)
|
||||||
|
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type osaMsg struct {
|
||||||
|
Dialog bool
|
||||||
|
Text string
|
||||||
|
Message string
|
||||||
|
As string
|
||||||
|
Title string
|
||||||
|
Icon string
|
||||||
|
Buttons []string
|
||||||
|
Cancel int
|
||||||
|
Default int
|
||||||
|
}
|
66
msg_linux.go
Normal file
66
msg_linux.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
import "os/exec"
|
||||||
|
|
||||||
|
func Error(text string, options ...Option) (bool, error) {
|
||||||
|
return message("--error", text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(text string, options ...Option) (bool, error) {
|
||||||
|
return message("--info", text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Question(text string, options ...Option) (bool, error) {
|
||||||
|
return message("--question", text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warning(text string, options ...Option) (bool, error) {
|
||||||
|
return message("--warning", text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func message(arg, text string, options []Option) (bool, error) {
|
||||||
|
opts := optsParse(options)
|
||||||
|
|
||||||
|
args := []string{arg, "--text", text, "--no-markup"}
|
||||||
|
if opts.title != "" {
|
||||||
|
args = append(args, "--title", opts.title)
|
||||||
|
}
|
||||||
|
if opts.ok != "" {
|
||||||
|
args = append(args, "--ok-label", opts.ok)
|
||||||
|
}
|
||||||
|
if opts.cancel != "" {
|
||||||
|
args = append(args, "--cancel-label", opts.cancel)
|
||||||
|
}
|
||||||
|
if opts.extra != "" {
|
||||||
|
args = append(args, "--extra-button", opts.extra)
|
||||||
|
}
|
||||||
|
if opts.nowrap {
|
||||||
|
args = append(args, "--no-wrap")
|
||||||
|
}
|
||||||
|
if opts.ellipsize {
|
||||||
|
args = append(args, "--ellipsize")
|
||||||
|
}
|
||||||
|
if opts.defcancel {
|
||||||
|
args = append(args, "--default-cancel")
|
||||||
|
}
|
||||||
|
switch opts.icon {
|
||||||
|
case ErrorIcon:
|
||||||
|
args = append(args, "--icon-name=dialog-error")
|
||||||
|
case InfoIcon:
|
||||||
|
args = append(args, "--icon-name=dialog-information")
|
||||||
|
case QuestionIcon:
|
||||||
|
args = append(args, "--icon-name=dialog-question")
|
||||||
|
case WarningIcon:
|
||||||
|
args = append(args, "--icon-name=dialog-warning")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("zenity", args...)
|
||||||
|
_, err := cmd.Output()
|
||||||
|
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, err
|
||||||
|
}
|
43
msg_test.go
Normal file
43
msg_test.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestError(t *testing.T) {
|
||||||
|
res, err := Error("An error has occured.", Title("Error"), Icon(ErrorIcon))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
t.Logf("%#v", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInfo(t *testing.T) {
|
||||||
|
res, err := Info("All updates are complete.", Title("Information"), Icon(InfoIcon))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
t.Logf("%#v", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWarning(t *testing.T) {
|
||||||
|
res, err := Warning("Are you sure you want to proceed?", Title("Warning"), Icon(WarningIcon))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
t.Logf("%#v", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuestion(t *testing.T) {
|
||||||
|
res, err := Question("Are you sure you want to proceed?", Title("Question"), Icon(QuestionIcon))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
t.Logf("%#v", res)
|
||||||
|
}
|
||||||
|
}
|
65
msg_windows.go
Normal file
65
msg_windows.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
user32 = syscall.NewLazyDLL("user32.dll")
|
||||||
|
messageBox = user32.NewProc("MessageBoxW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Error(text string, options ...Option) (bool, error) {
|
||||||
|
return message(0, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(text string, options ...Option) (bool, error) {
|
||||||
|
return message(1, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Question(text string, options ...Option) (bool, error) {
|
||||||
|
return message(2, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warning(text string, options ...Option) (bool, error) {
|
||||||
|
return message(3, text, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
func message(dialog int, text string, options []Option) (bool, error) {
|
||||||
|
opts := optsParse(options)
|
||||||
|
|
||||||
|
var flags, caption uintptr
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case dialog == 2 && opts.extra != "":
|
||||||
|
flags |= 0x3 // MB_YESNOCANCEL
|
||||||
|
case dialog == 2 || opts.extra != "":
|
||||||
|
flags |= 0x1 // MB_OKCANCEL
|
||||||
|
}
|
||||||
|
|
||||||
|
switch opts.icon {
|
||||||
|
case ErrorIcon:
|
||||||
|
flags |= 0x10 // MB_ICONERROR
|
||||||
|
case QuestionIcon:
|
||||||
|
flags |= 0x20 // MB_ICONQUESTION
|
||||||
|
case WarningIcon:
|
||||||
|
flags |= 0x30 // MB_ICONWARNING
|
||||||
|
case InfoIcon:
|
||||||
|
flags |= 0x40 // MB_ICONINFORMATION
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.title != "" {
|
||||||
|
caption = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(opts.title)))
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _, err := messageBox.Call(0,
|
||||||
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
||||||
|
caption, flags)
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return false, err
|
||||||
|
} else {
|
||||||
|
return n == 1 /* IDOK */ || n == 6 /* IDYES */, nil
|
||||||
|
}
|
||||||
|
}
|
22
osa_darwin.go
Normal file
22
osa_darwin.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run osa_scripts/generate.go osa_scripts/
|
||||||
|
|
||||||
|
func osaRun(script string, data interface{}) ([]byte, error) {
|
||||||
|
var buf strings.Builder
|
||||||
|
|
||||||
|
err := osaScripts.ExecuteTemplate(&buf, script, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = buf.String()
|
||||||
|
cmd := exec.Command("osascript", "-l", "JavaScript")
|
||||||
|
cmd.Stdin = strings.NewReader(res[len("<script>") : len(res)-len("</script>")])
|
||||||
|
return cmd.Output()
|
||||||
|
}
|
28
osa_scripts/file.gots
Normal file
28
osa_scripts/file.gots
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
var app = Application.currentApplication()
|
||||||
|
app.includeStandardAdditions = true
|
||||||
|
app.activate()
|
||||||
|
|
||||||
|
var opts = {}
|
||||||
|
opts.withPrompt = {{.Prompt}}
|
||||||
|
opts.multipleSelectionsAllowed = {{.Multiple}}
|
||||||
|
|
||||||
|
{{if .Location -}}
|
||||||
|
opts.defaultLocation = {{.Location}}
|
||||||
|
{{end -}}
|
||||||
|
{{if .Type -}}
|
||||||
|
opts.ofType = {{.Type}}
|
||||||
|
{{end -}}
|
||||||
|
|
||||||
|
var res
|
||||||
|
try {
|
||||||
|
res = app[{{.Operation}}](opts)
|
||||||
|
} catch (e) {
|
||||||
|
if (e.errorNumber !== -128) throw e
|
||||||
|
}
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
res.join('\0')
|
||||||
|
} else if (res != null) {
|
||||||
|
res.toString()
|
||||||
|
} else {
|
||||||
|
void 0
|
||||||
|
}
|
76
osa_scripts/generate.go
Normal file
76
osa_scripts/generate.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dir := os.Args[1]
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var str strings.Builder
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if name := file.Name(); filepath.Ext(name) == ".gots" {
|
||||||
|
str.WriteString("\n" + `{{define "`)
|
||||||
|
str.WriteString(strings.TrimSuffix(name, ".gots"))
|
||||||
|
str.WriteString(`"}}<script>`)
|
||||||
|
|
||||||
|
func() {
|
||||||
|
in, err := os.Open(filepath.Join(dir, name))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(in)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line != "" {
|
||||||
|
str.WriteString(line)
|
||||||
|
str.WriteRune('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
str.WriteString("</script>{{end}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create("osa_gen_darwin.go")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = generator.Execute(out, str.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = out.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var generator = template.Must(template.New("").Parse(`// Code generated by zenity; DO NOT EDIT.
|
||||||
|
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
import "html/template"
|
||||||
|
|
||||||
|
var osaScripts = template.Must(template.New("").Parse(` + "`{{.}}`" + `))
|
||||||
|
`))
|
33
osa_scripts/msg.gots
Normal file
33
osa_scripts/msg.gots
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
var app = Application.currentApplication()
|
||||||
|
app.includeStandardAdditions = true
|
||||||
|
app.activate()
|
||||||
|
|
||||||
|
var opts = {}
|
||||||
|
|
||||||
|
{{if .Buttons -}}
|
||||||
|
opts.buttons = {{.Buttons}}
|
||||||
|
{{end -}}
|
||||||
|
{{if .Default -}}
|
||||||
|
opts.defaultButton = {{.Default}}
|
||||||
|
{{end -}}
|
||||||
|
{{if .Cancel -}}
|
||||||
|
opts.cancelButton = {{.Cancel}}
|
||||||
|
{{end -}}
|
||||||
|
|
||||||
|
{{if .Dialog -}}
|
||||||
|
{{if .Title -}}
|
||||||
|
opts.withTitle = {{.Title}}
|
||||||
|
{{end -}}
|
||||||
|
{{if .Icon -}}
|
||||||
|
opts.withIcon = {{.Icon}}
|
||||||
|
{{end -}}
|
||||||
|
app.displayDialog({{.Text}}, opts)
|
||||||
|
{{else -}}
|
||||||
|
{{if .As -}}
|
||||||
|
opts.as = {{.As}}
|
||||||
|
{{end -}}
|
||||||
|
{{if .Message -}}
|
||||||
|
opts.message = {{.Message}}
|
||||||
|
{{end -}}
|
||||||
|
app.displayAlert({{.Text}}, opts)
|
||||||
|
{{end -}}
|
109
zenity.go
Normal file
109
zenity.go
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
package zenity
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
// General options
|
||||||
|
title string
|
||||||
|
|
||||||
|
// File selection options
|
||||||
|
filename string
|
||||||
|
overwrite bool
|
||||||
|
filters []FileFilter
|
||||||
|
|
||||||
|
// Message options
|
||||||
|
icon MessageIcon
|
||||||
|
ok string
|
||||||
|
cancel string
|
||||||
|
extra string
|
||||||
|
nowrap bool
|
||||||
|
ellipsize bool
|
||||||
|
defcancel bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*options)
|
||||||
|
|
||||||
|
func optsParse(options []Option) (res options) {
|
||||||
|
for _, o := range options {
|
||||||
|
o(&res)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// General options
|
||||||
|
|
||||||
|
func Title(title string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.title = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File selection options
|
||||||
|
|
||||||
|
func Filename(filename string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.filename = filename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfirmOverwrite(o *options) {
|
||||||
|
o.overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileFilter struct {
|
||||||
|
Name string
|
||||||
|
Exts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileFilters []FileFilter
|
||||||
|
|
||||||
|
func (f FileFilters) New() Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.filters = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message options
|
||||||
|
|
||||||
|
type MessageIcon int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrorIcon MessageIcon = iota + 1
|
||||||
|
InfoIcon
|
||||||
|
QuestionIcon
|
||||||
|
WarningIcon
|
||||||
|
)
|
||||||
|
|
||||||
|
func Icon(icon MessageIcon) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.icon = icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OKLabel(ok string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.ok = ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CancelLabel(cancel string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.cancel = cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtraButton(extra string) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.extra = extra
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NoWrap(o *options) {
|
||||||
|
o.nowrap = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ellipsize(o *options) {
|
||||||
|
o.ellipsize = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultCancel(o *options) {
|
||||||
|
o.defcancel = true
|
||||||
|
}
|
Loading…
Reference in a new issue