Merge pull request #6 from ncruces/dev

Defaults and other fixes
This commit is contained in:
Nuno Cruces 2021-03-04 13:26:27 +00:00
commit 93e49a6b03
22 changed files with 252 additions and 202 deletions

View file

@ -17,6 +17,10 @@ import (
//go:generate go run github.com/josephspurrier/goversioninfo/cmd/goversioninfo -platform-specific -manifest=win.manifest //go:generate go run github.com/josephspurrier/goversioninfo/cmd/goversioninfo -platform-specific -manifest=win.manifest
const (
unspecified = "\x00"
)
var ( var (
// Application Options // Application Options
notification bool notification bool
@ -122,14 +126,14 @@ func setupFlags() {
flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog") flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog")
// General options // General options
flag.StringVar(&title, "title", "", "Set the dialog title") flag.StringVar(&title, "title", "", "Set the dialog `title`")
flag.StringVar(&icon, "window-icon", "", "Set the window icon (error, info, question, warning)") flag.StringVar(&icon, "window-icon", "", "Set the window `icon` (error, info, question, warning)")
flag.UintVar(&width, "width", 0, "Set the width") flag.UintVar(&width, "width", 0, "Set the `width`")
flag.UintVar(&height, "height", 0, "Set the height") flag.UintVar(&height, "height", 0, "Set the `height`")
// Message options // Message options
flag.StringVar(&text, "text", "", "Set the dialog text") flag.StringVar(&text, "text", "", "Set the dialog `text`")
flag.StringVar(&icon, "icon-name", "", "Set the dialog icon (dialog-error, dialog-information, dialog-question, dialog-warning)") flag.StringVar(&icon, "icon-name", "", "Set the dialog `icon` (dialog-error, dialog-information, dialog-question, dialog-warning)")
flag.StringVar(&okLabel, "ok-label", "", "Set the label of the OK button") flag.StringVar(&okLabel, "ok-label", "", "Set the label of the OK button")
flag.StringVar(&cancelLabel, "cancel-label", "", "Set the label of the Cancel button") flag.StringVar(&cancelLabel, "cancel-label", "", "Set the label of the Cancel button")
flag.StringVar(&extraButton, "extra-button", "", "Add an extra button") flag.StringVar(&extraButton, "extra-button", "", "Add an extra button")
@ -144,11 +148,11 @@ func setupFlags() {
flag.BoolVar(&confirmOverwrite, "confirm-overwrite", false, "Confirm file selection if filename already exists") flag.BoolVar(&confirmOverwrite, "confirm-overwrite", false, "Confirm file selection if filename already exists")
flag.BoolVar(&confirmCreate, "confirm-create", false, "Confirm file selection if filename does not yet exist (Windows only)") flag.BoolVar(&confirmCreate, "confirm-create", false, "Confirm file selection if filename does not yet exist (Windows only)")
flag.BoolVar(&showHidden, "show-hidden", false, "Show hidden files (Windows and macOS only)") flag.BoolVar(&showHidden, "show-hidden", false, "Show hidden files (Windows and macOS only)")
flag.StringVar(&filename, "filename", "", "Set the filename") flag.StringVar(&filename, "filename", "", "Set the `filename`")
flag.Var(&fileFilters, "file-filter", "Set a filename filter (NAME | PATTERN1 PATTERN2 ...)") flag.Var(&fileFilters, "file-filter", "Set a filename filter (NAME | PATTERN1 PATTERN2 ...)")
// Color selection options // Color selection options
flag.StringVar(&defaultColor, "color", "", "Set the color") flag.StringVar(&defaultColor, "color", "", "Set the `color`")
flag.BoolVar(&showPalette, "show-palette", false, "Show the palette") flag.BoolVar(&showPalette, "show-palette", false, "Show the palette")
// Windows specific options // Windows specific options
@ -157,9 +161,17 @@ func setupFlags() {
flag.BoolVar(&wslpath, "wslpath", false, "Use wslpath for path translation (Windows only)") flag.BoolVar(&wslpath, "wslpath", false, "Use wslpath for path translation (Windows only)")
} }
// Internal options // Command options
flag.IntVar(&zenutil.Timeout, "timeout", 0, "Set dialog timeout in seconds") flag.IntVar(&zenutil.Timeout, "timeout", 0, "Set dialog `timeout` in seconds")
flag.StringVar(&zenutil.Separator, "separator", "|", "Set output separator character") flag.StringVar(&zenutil.Separator, "separator", "|", "Set output `separator` character")
// Detect unspecified values
title = unspecified
icon = unspecified
text = unspecified
okLabel = unspecified
cancelLabel = unspecified
extraButton = unspecified
} }
func validateFlags() { func validateFlags() {
@ -193,26 +205,53 @@ func validateFlags() {
func loadFlags() []zenity.Option { func loadFlags() []zenity.Option {
var opts []zenity.Option var opts []zenity.Option
// Defaults
setDefault := func(s *string, val string) {
if *s == unspecified {
*s = val
}
}
switch {
case errorDlg:
setDefault(&title, "Error")
setDefault(&icon, "dialog-error")
setDefault(&text, "An error has occurred.")
setDefault(&okLabel, "OK")
case infoDlg:
setDefault(&title, "Information")
setDefault(&icon, "dialog-information")
setDefault(&text, "All updates are complete.")
setDefault(&okLabel, "OK")
case warningDlg:
setDefault(&title, "Warning")
setDefault(&icon, "dialog-warning")
setDefault(&text, "Are you sure you want to proceed?")
setDefault(&okLabel, "OK")
case questionDlg:
setDefault(&title, "Question")
setDefault(&icon, "dialog-question")
setDefault(&text, "Are you sure you want to proceed?")
setDefault(&okLabel, "Yes")
setDefault(&cancelLabel, "No")
default:
setDefault(&text, "")
}
// General options // General options
if title != unspecified {
opts = append(opts, zenity.Title(title)) opts = append(opts, zenity.Title(title))
}
opts = append(opts, zenity.Width(width)) opts = append(opts, zenity.Width(width))
opts = append(opts, zenity.Height(height)) opts = append(opts, zenity.Height(height))
// Message options // Message options
var ico zenity.DialogIcon var ico zenity.DialogIcon
switch {
case errorDlg:
ico = zenity.ErrorIcon
case infoDlg:
ico = zenity.InfoIcon
case warningDlg:
ico = zenity.WarningIcon
case questionDlg:
ico = zenity.QuestionIcon
}
switch icon { switch icon {
case "":
ico = zenity.NoIcon
case "error", "dialog-error": case "error", "dialog-error":
ico = zenity.ErrorIcon ico = zenity.ErrorIcon
case "info", "dialog-information": case "info", "dialog-information":
@ -224,9 +263,15 @@ func loadFlags() []zenity.Option {
} }
opts = append(opts, zenity.Icon(ico)) opts = append(opts, zenity.Icon(ico))
if okLabel != unspecified {
opts = append(opts, zenity.OKLabel(okLabel)) opts = append(opts, zenity.OKLabel(okLabel))
}
if cancelLabel != unspecified {
opts = append(opts, zenity.CancelLabel(cancelLabel)) opts = append(opts, zenity.CancelLabel(cancelLabel))
}
if extraButton != unspecified {
opts = append(opts, zenity.ExtraButton(extraButton)) opts = append(opts, zenity.ExtraButton(extraButton))
}
if noWrap { if noWrap {
opts = append(opts, zenity.NoWrap()) opts = append(opts, zenity.NoWrap())
} }

View file

@ -8,7 +8,7 @@ import "image/color"
// //
// Valid options: Title, Color, ShowPalette. // Valid options: Title, Color, ShowPalette.
func SelectColor(options ...Option) (color.Color, error) { func SelectColor(options ...Option) (color.Color, error) {
return selectColor(options) return selectColor(applyOptions(options))
} }
// Color returns an Option to set the color. // Color returns an Option to set the color.

View file

@ -7,9 +7,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func selectColor(options []Option) (color.Color, error) { func selectColor(opts options) (color.Color, error) {
opts := applyOptions(options)
var col color.Color var col color.Color
if opts.color != nil { if opts.color != nil {
col = opts.color col = opts.color

View file

@ -9,13 +9,11 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func selectColor(options []Option) (color.Color, error) { func selectColor(opts options) (color.Color, error) {
opts := applyOptions(options)
args := []string{"--color-selection"} args := []string{"--color-selection"}
if opts.title != "" { if opts.title != nil {
args = append(args, "--title", opts.title) args = append(args, "--title", *opts.title)
} }
if opts.color != nil { if opts.color != nil {
args = append(args, "--color", zenutil.UnparseColor(opts.color)) args = append(args, "--color", zenutil.UnparseColor(opts.color))

View file

@ -20,9 +20,7 @@ func init() {
} }
} }
func selectColor(options []Option) (color.Color, error) { func selectColor(opts options) (color.Color, error) {
opts := applyOptions(options)
// load custom colors // load custom colors
colorsMutex.Lock() colorsMutex.Lock()
customColors := savedColors customColors := savedColors
@ -46,7 +44,7 @@ func selectColor(options []Option) (color.Color, error) {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
if opts.ctx != nil || opts.title != "" { if opts.ctx != nil || opts.title != nil {
unhook, err := hookDialogTitle(opts.ctx, opts.title) unhook, err := hookDialogTitle(opts.ctx, opts.title)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -11,7 +11,7 @@ import (
// //
// Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s). // Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s).
func SelectFile(options ...Option) (string, error) { func SelectFile(options ...Option) (string, error) {
return selectFile(options) return selectFile(applyOptions(options))
} }
// SelectFileMutiple displays the multiple file selection dialog. // SelectFileMutiple displays the multiple file selection dialog.
@ -20,7 +20,7 @@ func SelectFile(options ...Option) (string, error) {
// //
// Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s). // Valid options: Title, Directory, Filename, ShowHidden, FileFilter(s).
func SelectFileMutiple(options ...Option) ([]string, error) { func SelectFileMutiple(options ...Option) ([]string, error) {
return selectFileMutiple(options) return selectFileMutiple(applyOptions(options))
} }
// SelectFileSave displays the save file selection dialog. // SelectFileSave displays the save file selection dialog.
@ -30,7 +30,7 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
// Valid options: Title, Filename, ConfirmOverwrite, ConfirmCreate, ShowHidden, // Valid options: Title, Filename, ConfirmOverwrite, ConfirmCreate, ShowHidden,
// FileFilter(s). // FileFilter(s).
func SelectFileSave(options ...Option) (string, error) { func SelectFileSave(options ...Option) (string, error) {
return selectFileSave(options) return selectFileSave(applyOptions(options))
} }
// Filename returns an Option to set the filename. // Filename returns an Option to set the filename.

View file

@ -7,9 +7,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func selectFile(options []Option) (string, error) { func selectFile(opts options) (string, error) {
opts := applyOptions(options)
var data zenutil.File var data zenutil.File
data.Options.Prompt = opts.title data.Options.Prompt = opts.title
data.Options.Invisibles = opts.showHidden data.Options.Invisibles = opts.showHidden
@ -35,9 +33,7 @@ func selectFile(options []Option) (string, error) {
return string(out), nil return string(out), nil
} }
func selectFileMutiple(options []Option) ([]string, error) { func selectFileMutiple(opts options) ([]string, error) {
opts := applyOptions(options)
var data zenutil.File var data zenutil.File
data.Options.Prompt = opts.title data.Options.Prompt = opts.title
data.Options.Invisibles = opts.showHidden data.Options.Invisibles = opts.showHidden
@ -68,9 +64,7 @@ func selectFileMutiple(options []Option) ([]string, error) {
return strings.Split(string(out), zenutil.Separator), nil return strings.Split(string(out), zenutil.Separator), nil
} }
func selectFileSave(options []Option) (string, error) { func selectFileSave(opts options) (string, error) {
opts := applyOptions(options)
var data zenutil.File var data zenutil.File
data.Options.Prompt = opts.title data.Options.Prompt = opts.title
data.Options.Invisibles = opts.showHidden data.Options.Invisibles = opts.showHidden

View file

@ -9,15 +9,13 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func selectFile(options []Option) (string, error) { func selectFile(opts options) (string, error) {
opts := applyOptions(options)
args := []string{"--file-selection"} args := []string{"--file-selection"}
if opts.directory { if opts.directory {
args = append(args, "--directory") args = append(args, "--directory")
} }
if opts.title != "" { if opts.title != nil {
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)
@ -37,15 +35,13 @@ func selectFile(options []Option) (string, error) {
return string(out), nil return string(out), nil
} }
func selectFileMutiple(options []Option) ([]string, error) { func selectFileMutiple(opts options) ([]string, error) {
opts := applyOptions(options)
args := []string{"--file-selection", "--multiple", "--separator", zenutil.Separator} args := []string{"--file-selection", "--multiple", "--separator", zenutil.Separator}
if opts.directory { if opts.directory {
args = append(args, "--directory") args = append(args, "--directory")
} }
if opts.title != "" { if opts.title != nil {
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)
@ -65,15 +61,13 @@ func selectFileMutiple(options []Option) ([]string, error) {
return strings.Split(string(out), zenutil.Separator), nil return strings.Split(string(out), zenutil.Separator), nil
} }
func selectFileSave(options []Option) (string, error) { func selectFileSave(opts options) (string, error) {
opts := applyOptions(options)
args := []string{"--file-selection", "--save"} args := []string{"--file-selection", "--save"}
if opts.directory { if opts.directory {
args = append(args, "--directory") args = append(args, "--directory")
} }
if opts.title != "" { if opts.title != nil {
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)

View file

@ -17,8 +17,7 @@ var (
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName") shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
) )
func selectFile(options []Option) (string, error) { func selectFile(opts options) (string, error) {
opts := applyOptions(options)
if opts.directory { if opts.directory {
res, _, err := pickFolders(opts, false) res, _, err := pickFolders(opts, false)
return res, err return res, err
@ -28,8 +27,8 @@ func selectFile(options []Option) (string, error) {
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Flags = 0x81008 // OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST|OFN_EXPLORER args.Flags = 0x81008 // OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST|OFN_EXPLORER
if opts.title != "" { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
@ -65,8 +64,7 @@ func selectFile(options []Option) (string, error) {
return syscall.UTF16ToString(res[:]), nil return syscall.UTF16ToString(res[:]), nil
} }
func selectFileMutiple(options []Option) ([]string, error) { func selectFileMutiple(opts options) ([]string, error) {
opts := applyOptions(options)
if opts.directory { if opts.directory {
_, res, err := pickFolders(opts, true) _, res, err := pickFolders(opts, true)
return res, err return res, err
@ -76,8 +74,8 @@ func selectFileMutiple(options []Option) ([]string, error) {
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Flags = 0x81208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_FILEMUSTEXIST|OFN_EXPLORER args.Flags = 0x81208 // OFN_NOCHANGEDIR|OFN_ALLOWMULTISELECT|OFN_FILEMUSTEXIST|OFN_EXPLORER
if opts.title != "" { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.showHidden { if opts.showHidden {
args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN args.Flags |= 0x10000000 // OFN_FORCESHOWHIDDEN
@ -138,8 +136,7 @@ func selectFileMutiple(options []Option) ([]string, error) {
return split, nil return split, nil
} }
func selectFileSave(options []Option) (string, error) { func selectFileSave(opts options) (string, error) {
opts := applyOptions(options)
if opts.directory { if opts.directory {
res, _, err := pickFolders(opts, false) res, _, err := pickFolders(opts, false)
return res, err return res, err
@ -149,8 +146,8 @@ func selectFileSave(options []Option) (string, error) {
args.StructSize = uint32(unsafe.Sizeof(args)) args.StructSize = uint32(unsafe.Sizeof(args))
args.Flags = 0x88808 // OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST|OFN_NOREADONLYRETURN|OFN_EXPLORER args.Flags = 0x88808 // OFN_NOCHANGEDIR|OFN_PATHMUSTEXIST|OFN_NOREADONLYRETURN|OFN_EXPLORER
if opts.title != "" { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.confirmOverwrite { if opts.confirmOverwrite {
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
@ -229,8 +226,8 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
return "", nil, syscall.Errno(hr) return "", nil, syscall.Errno(hr)
} }
if opts.title != "" { if opts.title != nil {
ptr := syscall.StringToUTF16Ptr(opts.title) ptr := syscall.StringToUTF16Ptr(*opts.title)
dialog.Call(dialog.vtbl.SetTitle, uintptr(unsafe.Pointer(ptr))) dialog.Call(dialog.vtbl.SetTitle, uintptr(unsafe.Pointer(ptr)))
} }
@ -319,8 +316,8 @@ func browseForFolder(opts options) (string, []string, error) {
var args _BROWSEINFO var args _BROWSEINFO
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
if opts.title != "" { if opts.title != nil {
args.Title = syscall.StringToUTF16Ptr(opts.title) args.Title = syscall.StringToUTF16Ptr(*opts.title)
} }
if opts.filename != "" { if opts.filename != "" {
ptr := syscall.StringToUTF16Ptr(opts.filename) ptr := syscall.StringToUTF16Ptr(opts.filename)

View file

@ -28,10 +28,13 @@ if(Array.isArray(res)){res.join({{json .Separator}})}else{res.toString()}
var app=Application.currentApplication() var app=Application.currentApplication()
app.includeStandardAdditions=true app.includeStandardAdditions=true
app.activate() app.activate()
ObjC.import("stdlib")
ObjC.import("stdio")
var res=app.{{.Operation}}({{json .Text}},{{json .Options}}) var res=app.{{.Operation}}({{json .Text}},{{json .Options}})
if(res.gaveUp){ObjC.import("stdlib") if(res.gaveUp){$.exit(5)}
$.exit(5)} if(res.buttonReturned==={{json .Extra}}){$.puts(res.buttonReturned)
if(res.buttonReturned==={{json .Extra}}){res.buttonReturned}else{void 0} $.exit(1)}
void 0
{{- end}} {{- end}}
{{define "notify" -}} {{define "notify" -}}
var app=Application.currentApplication() var app=Application.currentApplication()

View file

@ -2,13 +2,14 @@ var app = Application.currentApplication()
app.includeStandardAdditions = true app.includeStandardAdditions = true
app.activate() app.activate()
ObjC.import("stdlib")
ObjC.import("stdio")
var res = app.{{.Operation}}({{json .Text}}, {{json .Options}}) var res = app.{{.Operation}}({{json .Text}}, {{json .Options}})
if (res.gaveUp) { if (res.gaveUp) {
ObjC.import("stdlib")
$.exit(5) $.exit(5)
} }
if (res.buttonReturned === {{json .Extra}}) { if (res.buttonReturned === {{json .Extra}}) {
res.buttonReturned $.puts(res.buttonReturned)
} else { $.exit(1)
void 0
} }

View file

@ -56,7 +56,7 @@ type File struct {
// FileOptions is internal. // FileOptions is internal.
type FileOptions struct { type FileOptions struct {
Prompt string `json:"withPrompt,omitempty"` Prompt *string `json:"withPrompt,omitempty"`
Type []string `json:"ofType,omitempty"` Type []string `json:"ofType,omitempty"`
Name string `json:"defaultName,omitempty"` Name string `json:"defaultName,omitempty"`
Location string `json:"defaultLocation,omitempty"` Location string `json:"defaultLocation,omitempty"`
@ -68,7 +68,7 @@ type FileOptions struct {
type Msg struct { type Msg struct {
Operation string Operation string
Text string Text string
Extra string Extra *string
Options MsgOptions Options MsgOptions
} }
@ -76,7 +76,7 @@ type Msg struct {
type MsgOptions struct { type MsgOptions struct {
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
As string `json:"as,omitempty"` As string `json:"as,omitempty"`
Title string `json:"withTitle,omitempty"` Title *string `json:"withTitle,omitempty"`
Icon string `json:"withIcon,omitempty"` Icon string `json:"withIcon,omitempty"`
Buttons []string `json:"buttons,omitempty"` Buttons []string `json:"buttons,omitempty"`
Cancel int `json:"cancelButton,omitempty"` Cancel int `json:"cancelButton,omitempty"`
@ -92,6 +92,6 @@ type Notify struct {
// NotifyOptions is internal. // NotifyOptions is internal.
type NotifyOptions struct { type NotifyOptions struct {
Title string `json:"withTitle,omitempty"` Title *string `json:"withTitle,omitempty"`
Subtitle string `json:"subtitle,omitempty"` Subtitle string `json:"subtitle,omitempty"`
} }

22
msg.go
View file

@ -2,7 +2,7 @@ package zenity
// ErrExtraButton is returned by dialog functions when the extra button is // ErrExtraButton is returned by dialog functions when the extra button is
// pressed. // pressed.
const ErrExtraButton = constError("Extra button pressed") const ErrExtraButton = stringErr("Extra button pressed")
// Question displays the question dialog. // Question displays the question dialog.
// //
@ -11,7 +11,7 @@ const ErrExtraButton = constError("Extra button pressed")
// Valid options: Title, Width, Height, Icon, OKLabel, CancelLabel, // Valid options: Title, Width, Height, Icon, OKLabel, CancelLabel,
// ExtraButton, NoWrap, Ellipsize, DefaultCancel. // ExtraButton, NoWrap, Ellipsize, DefaultCancel.
func Question(text string, options ...Option) (bool, error) { func Question(text string, options ...Option) (bool, error) {
return message(questionKind, text, options) return message(questionKind, text, applyOptions(options))
} }
// Info displays the info dialog. // Info displays the info dialog.
@ -21,7 +21,7 @@ func Question(text string, options ...Option) (bool, error) {
// Valid options: Title, Width, Height, Icon, OKLabel, ExtraButton, // Valid options: Title, Width, Height, Icon, OKLabel, ExtraButton,
// NoWrap, Ellipsize. // NoWrap, Ellipsize.
func Info(text string, options ...Option) (bool, error) { func Info(text string, options ...Option) (bool, error) {
return message(infoKind, text, options) return message(infoKind, text, applyOptions(options))
} }
// Warning displays the warning dialog. // Warning displays the warning dialog.
@ -31,7 +31,7 @@ func Info(text string, options ...Option) (bool, error) {
// Valid options: Title, Width, Height, Icon, OKLabel, ExtraButton, // Valid options: Title, Width, Height, Icon, OKLabel, ExtraButton,
// NoWrap, Ellipsize. // NoWrap, Ellipsize.
func Warning(text string, options ...Option) (bool, error) { func Warning(text string, options ...Option) (bool, error) {
return message(warningKind, text, options) return message(warningKind, text, applyOptions(options))
} }
// Error displays the error dialog. // Error displays the error dialog.
@ -41,7 +41,7 @@ func Warning(text string, options ...Option) (bool, error) {
// Valid options: Title, Width, Height, Icon, OKLabel, ExtraButton, // Valid options: Title, Width, Height, Icon, OKLabel, ExtraButton,
// NoWrap, Ellipsize. // NoWrap, Ellipsize.
func Error(text string, options ...Option) (bool, error) { func Error(text string, options ...Option) (bool, error) {
return message(errorKind, text, options) return message(errorKind, text, applyOptions(options))
} }
type messageKind int type messageKind int
@ -55,30 +55,30 @@ const (
// OKLabel returns an Option to set the label of the OK button. // OKLabel returns an Option to set the label of the OK button.
func OKLabel(ok string) Option { func OKLabel(ok string) Option {
return funcOption(func(o *options) { o.okLabel = ok }) return funcOption(func(o *options) { o.okLabel = &ok })
} }
// CancelLabel returns an Option to set the label of the Cancel button. // CancelLabel returns an Option to set the label of the Cancel button.
func CancelLabel(cancel string) Option { func CancelLabel(cancel string) Option {
return funcOption(func(o *options) { o.cancelLabel = cancel }) return funcOption(func(o *options) { o.cancelLabel = &cancel })
} }
// ExtraButton returns an Option to add an extra button. // ExtraButton returns an Option to add an extra button.
func ExtraButton(extra string) Option { func ExtraButton(extra string) Option {
return funcOption(func(o *options) { o.extraButton = extra }) return funcOption(func(o *options) { o.extraButton = &extra })
} }
// NoWrap returns an Option to disable enable text wrapping. // NoWrap returns an Option to disable enable text wrapping (Unix only).
func NoWrap() Option { func NoWrap() Option {
return funcOption(func(o *options) { o.noWrap = true }) return funcOption(func(o *options) { o.noWrap = true })
} }
// Ellipsize returns an Option to enable ellipsizing in the dialog text. // Ellipsize returns an Option to enable ellipsizing in the dialog text (Unix only).
func Ellipsize() Option { func Ellipsize() Option {
return funcOption(func(o *options) { o.ellipsize = true }) return funcOption(func(o *options) { o.ellipsize = true })
} }
// DefaultCancel returns an Option to give Cancel button focus by default. // DefaultCancel returns an Option to give the Cancel button focus by default.
func DefaultCancel() Option { func DefaultCancel() Option {
return funcOption(func(o *options) { o.defaultCancel = true }) return funcOption(func(o *options) { o.defaultCancel = true })
} }

View file

@ -6,14 +6,18 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func message(kind messageKind, text string, options []Option) (bool, error) { func message(kind messageKind, text string, opts options) (bool, error) {
opts := applyOptions(options)
var data zenutil.Msg var data zenutil.Msg
data.Text = text data.Text = text
data.Options.Timeout = zenutil.Timeout data.Options.Timeout = zenutil.Timeout
dialog := kind == questionKind || opts.icon != 0 // dialog is more flexible, alert prettier
var dialog bool
if opts.icon != 0 { // use if we want to show a specific icon
dialog = true
} else if kind == questionKind && opts.cancelLabel == nil { // use for questions with default buttons
dialog = true
}
if dialog { if dialog {
data.Operation = "displayDialog" data.Operation = "displayDialog"
@ -29,9 +33,9 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
} }
} else { } else {
data.Operation = "displayAlert" data.Operation = "displayAlert"
if opts.title != "" { if opts.title != nil {
data.Text = *opts.title
data.Options.Message = text data.Options.Message = text
data.Text = opts.title
} }
switch kind { switch kind {
@ -44,58 +48,67 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
} }
} }
if kind != questionKind { if kind == questionKind {
if dialog { // alert defaults to a single button, we need two
opts.okLabel = "OK" if opts.cancelLabel == nil && !dialog {
opts.cancelLabel = stringPtr("Cancel")
} }
opts.cancelLabel = "" } else {
// dialog defaults to two buttons, we need one
if opts.okLabel == nil && dialog {
opts.okLabel = stringPtr("OK")
} }
if opts.okLabel != "" || opts.cancelLabel != "" || opts.extraButton != "" { // only questions have cancel
if opts.okLabel == "" { opts.cancelLabel = nil
opts.okLabel = "OK"
} }
if opts.cancelLabel == "" {
opts.cancelLabel = "Cancel" if opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil {
if opts.okLabel == nil {
opts.okLabel = stringPtr("OK")
} }
if kind == questionKind { if kind == questionKind {
if opts.extraButton == "" { if opts.cancelLabel == nil {
data.Options.Buttons = []string{opts.cancelLabel, opts.okLabel} opts.cancelLabel = stringPtr("Cancel")
}
if opts.extraButton == nil {
data.Options.Buttons = []string{*opts.cancelLabel, *opts.okLabel}
data.Options.Default = 2 data.Options.Default = 2
data.Options.Cancel = 1 data.Options.Cancel = 1
} else { } else {
data.Options.Buttons = []string{opts.extraButton, opts.cancelLabel, opts.okLabel} data.Options.Buttons = []string{*opts.extraButton, *opts.cancelLabel, *opts.okLabel}
data.Options.Default = 3 data.Options.Default = 3
data.Options.Cancel = 2 data.Options.Cancel = 2
} }
} else { } else {
if opts.extraButton == "" { if opts.extraButton == nil {
data.Options.Buttons = []string{opts.okLabel} data.Options.Buttons = []string{*opts.okLabel}
data.Options.Default = 1 data.Options.Default = 1
} else { } else {
data.Options.Buttons = []string{opts.extraButton, opts.okLabel} data.Options.Buttons = []string{*opts.extraButton, *opts.okLabel}
data.Options.Default = 2 data.Options.Default = 2
} }
} }
data.Extra = opts.extraButton data.Extra = opts.extraButton
} }
if opts.defaultCancel {
if kind == questionKind && opts.defaultCancel {
if data.Options.Cancel != 0 { if data.Options.Cancel != 0 {
data.Options.Default = data.Options.Cancel data.Options.Default = data.Options.Cancel
} } else {
if dialog && data.Options.Buttons == nil {
data.Options.Default = 1 data.Options.Default = 1
} }
} }
out, err := zenutil.Run(opts.ctx, "msg", data) out, err := zenutil.Run(opts.ctx, "msg", data)
if len(out) > 0 && opts.extraButton != nil &&
string(out[:len(out)-1]) == *opts.extraButton {
return false, ErrExtraButton
}
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 { if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
return false, nil return false, nil
} }
if err != nil { if err != nil {
return false, err return false, err
} }
if len(out) > 0 && string(out[:len(out)-1]) == opts.extraButton {
return false, ErrExtraButton
}
return true, err return true, err
} }

View file

@ -9,9 +9,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func message(kind messageKind, text string, options []Option) (bool, error) { func message(kind messageKind, text string, opts options) (bool, error) {
opts := applyOptions(options)
var args []string var args []string
switch kind { switch kind {
case questionKind: case questionKind:
@ -26,8 +24,8 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
if text != "" { if text != "" {
args = append(args, "--text", text, "--no-markup") args = append(args, "--text", text, "--no-markup")
} }
if opts.title != "" { if opts.title != nil {
args = append(args, "--title", opts.title) args = append(args, "--title", *opts.title)
} }
if opts.width > 0 { if opts.width > 0 {
args = append(args, "--width", strconv.FormatUint(uint64(opts.width), 10)) args = append(args, "--width", strconv.FormatUint(uint64(opts.width), 10))
@ -35,14 +33,14 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
if opts.height > 0 { if opts.height > 0 {
args = append(args, "--height", strconv.FormatUint(uint64(opts.height), 10)) args = append(args, "--height", strconv.FormatUint(uint64(opts.height), 10))
} }
if opts.okLabel != "" { if opts.okLabel != nil {
args = append(args, "--ok-label", opts.okLabel) args = append(args, "--ok-label", *opts.okLabel)
} }
if opts.cancelLabel != "" { if opts.cancelLabel != nil {
args = append(args, "--cancel-label", opts.cancelLabel) args = append(args, "--cancel-label", *opts.cancelLabel)
} }
if opts.extraButton != "" { if opts.extraButton != nil {
args = append(args, "--extra-button", opts.extraButton) args = append(args, "--extra-button", *opts.extraButton)
} }
if opts.noWrap { if opts.noWrap {
args = append(args, "--no-wrap") args = append(args, "--no-wrap")
@ -54,6 +52,8 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
args = append(args, "--default-cancel") args = append(args, "--default-cancel")
} }
switch opts.icon { switch opts.icon {
case NoIcon:
args = append(args, "--icon-name=")
case ErrorIcon: case ErrorIcon:
args = append(args, "--window-icon=error", "--icon-name=dialog-error") args = append(args, "--window-icon=error", "--icon-name=dialog-error")
case WarningIcon: case WarningIcon:
@ -65,10 +65,11 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
} }
out, err := zenutil.Run(opts.ctx, args) out, err := zenutil.Run(opts.ctx, args)
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() != 255 { if len(out) > 0 && opts.extraButton != nil &&
if len(out) > 0 && string(out[:len(out)-1]) == opts.extraButton { string(out[:len(out)-1]) == *opts.extraButton {
return false, ErrExtraButton return false, ErrExtraButton
} }
if err, ok := err.(*exec.ExitError); ok && err.ExitCode() == 1 {
return false, nil return false, nil
} }
if err != nil { if err != nil {

View file

@ -11,16 +11,16 @@ var (
messageBox = user32.NewProc("MessageBoxW") messageBox = user32.NewProc("MessageBoxW")
) )
func message(kind messageKind, text string, options []Option) (bool, error) { func message(kind messageKind, text string, opts options) (bool, error) {
opts := applyOptions(options)
var flags uintptr var flags uintptr
switch { switch {
case kind == questionKind && opts.extraButton != "": case kind == questionKind && opts.extraButton != nil:
flags |= 0x3 // MB_YESNOCANCEL flags |= 0x3 // MB_YESNOCANCEL
case kind == questionKind || opts.extraButton != "": case kind == questionKind:
flags |= 0x1 // MB_OKCANCEL flags |= 0x1 // MB_OKCANCEL
case opts.extraButton != nil:
flags |= 0x4 // MB_YESNO
} }
switch opts.icon { switch opts.icon {
@ -35,14 +35,14 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
} }
if kind == questionKind && opts.defaultCancel { if kind == questionKind && opts.defaultCancel {
if opts.extraButton == "" { if opts.extraButton == nil {
flags |= 0x100 // MB_DEFBUTTON2 flags |= 0x100 // MB_DEFBUTTON2
} else { } else {
flags |= 0x200 // MB_DEFBUTTON3 flags |= 0x200 // MB_DEFBUTTON3
} }
} }
if opts.ctx != nil || opts.okLabel != "" || opts.cancelLabel != "" || opts.extraButton != "" { if opts.ctx != nil || opts.okLabel != nil || opts.cancelLabel != nil || opts.extraButton != nil {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
@ -53,24 +53,29 @@ func message(kind messageKind, text string, options []Option) (bool, error) {
defer unhook() defer unhook()
} }
var title *uint16
if opts.title != nil {
title = syscall.StringToUTF16Ptr(*opts.title)
}
activate() activate()
s, _, err := messageBox.Call(0, s, _, err := messageBox.Call(0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(opts.title))), flags) uintptr(unsafe.Pointer(title)), flags)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return false, opts.ctx.Err() return false, opts.ctx.Err()
} }
if s == 0 { switch s {
case 1, 6: // IDOK, IDYES
return true, nil
case 2: // IDCANCEL
return false, nil
case 7: // IDNO
return false, ErrExtraButton
default:
return false, err return false, err
} }
if s == 7 || s == 2 && kind != questionKind { // IDNO
return false, ErrExtraButton
}
if s == 1 || s == 6 { // IDOK, IDYES
return true, nil
}
return false, nil
} }
func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFunc, err error) { func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFunc, err error) {
@ -81,23 +86,21 @@ func hookMessageLabels(kind messageKind, opts options) (unhook context.CancelFun
getClassName.Call(wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name))) getClassName.Call(wnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
if syscall.UTF16ToString(name[:]) == "Button" { if syscall.UTF16ToString(name[:]) == "Button" {
ctl, _, _ := getDlgCtrlID.Call(wnd) ctl, _, _ := getDlgCtrlID.Call(wnd)
var text string var text *string
switch ctl { switch ctl {
case 1, 6: // IDOK, IDYES case 1, 6: // IDOK, IDYES
text = opts.okLabel text = opts.okLabel
case 2: // IDCANCEL case 2: // IDCANCEL
if kind == questionKind { if kind == questionKind {
text = opts.cancelLabel text = opts.cancelLabel
} else if opts.extraButton != "" {
text = opts.extraButton
} else { } else {
text = opts.okLabel text = opts.okLabel
} }
case 7: // IDNO case 7: // IDNO
text = opts.extraButton text = opts.extraButton
} }
if text != "" { if text != nil {
ptr := syscall.StringToUTF16Ptr(text) ptr := syscall.StringToUTF16Ptr(*text)
setWindowText.Call(wnd, uintptr(unsafe.Pointer(ptr))) setWindowText.Call(wnd, uintptr(unsafe.Pointer(ptr)))
} }
} }

View file

@ -4,5 +4,5 @@ package zenity
// //
// Valid options: Title, Icon. // Valid options: Title, Icon.
func Notify(text string, options ...Option) error { func Notify(text string, options ...Option) error {
return notify(text, options) return notify(text, applyOptions(options))
} }

View file

@ -6,9 +6,7 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func notify(text string, options []Option) error { func notify(text string, opts options) error {
opts := applyOptions(options)
var data zenutil.Notify var data zenutil.Notify
data.Text = text data.Text = text
data.Options.Title = opts.title data.Options.Title = opts.title

View file

@ -6,26 +6,26 @@ import (
"github.com/ncruces/zenity/internal/zenutil" "github.com/ncruces/zenity/internal/zenutil"
) )
func notify(text string, options []Option) error { func notify(text string, opts options) error {
opts := applyOptions(options)
args := []string{"--notification"} args := []string{"--notification"}
if text != "" { if text != "" {
args = append(args, "--text", text, "--no-markup") args = append(args, "--text", text, "--no-markup")
} }
if opts.title != "" { if opts.title != nil {
args = append(args, "--title", opts.title) args = append(args, "--title", *opts.title)
} }
switch opts.icon { switch opts.icon {
case NoIcon:
args = append(args, "--window-icon=dialog")
case ErrorIcon: case ErrorIcon:
args = append(args, "--window-icon=error") args = append(args, "--window-icon=dialog-error")
case WarningIcon: case WarningIcon:
args = append(args, "--window-icon=warning") args = append(args, "--window-icon=dialog-warning")
case InfoIcon: case InfoIcon:
args = append(args, "--window-icon=info") args = append(args, "--window-icon=dialog-information")
case QuestionIcon: case QuestionIcon:
args = append(args, "--window-icon=question") args = append(args, "--window-icon=dialog-question")
} }
_, err := zenutil.Run(opts.ctx, args) _, err := zenutil.Run(opts.ctx, args)

View file

@ -13,9 +13,7 @@ var (
wtsSendMessage = wtsapi32.NewProc("WTSSendMessageW") wtsSendMessage = wtsapi32.NewProc("WTSSendMessageW")
) )
func notify(text string, options []Option) error { func notify(text string, opts options) error {
opts := applyOptions(options)
if opts.ctx != nil && opts.ctx.Err() != nil { if opts.ctx != nil && opts.ctx.Err() != nil {
return opts.ctx.Err() return opts.ctx.Err()
} }
@ -29,11 +27,13 @@ func notify(text string, options []Option) error {
info := syscall.StringToUTF16(text) info := syscall.StringToUTF16(text)
copy(args.Info[:len(args.Info)-1], info) copy(args.Info[:len(args.Info)-1], info)
title := syscall.StringToUTF16(opts.title) if opts.title != nil {
title := syscall.StringToUTF16(*opts.title)
copy(args.InfoTitle[:len(args.InfoTitle)-1], title) copy(args.InfoTitle[:len(args.InfoTitle)-1], title)
}
switch opts.icon { switch opts.icon {
case InfoIcon: case InfoIcon, QuestionIcon:
args.InfoFlags |= 0x1 // NIIF_INFO args.InfoFlags |= 0x1 // NIIF_INFO
case WarningIcon: case WarningIcon:
args.InfoFlags |= 0x2 // NIIF_WARNING args.InfoFlags |= 0x2 // NIIF_WARNING
@ -71,8 +71,8 @@ func wtsMessage(text string, opts options) error {
} }
title := opts.title title := opts.title
if title == "" { if title == nil {
title = "Notification" title = stringPtr("Notification")
} }
timeout := zenutil.Timeout timeout := zenutil.Timeout
@ -81,7 +81,7 @@ func wtsMessage(text string, opts options) error {
} }
ptext := syscall.StringToUTF16(text) ptext := syscall.StringToUTF16(text)
ptitle := syscall.StringToUTF16(title) ptitle := syscall.StringToUTF16(*title)
var res uint32 var res uint32
s, _, err := wtsSendMessage.Call( s, _, err := wtsSendMessage.Call(

View file

@ -138,11 +138,15 @@ func hookDialog(ctx context.Context, initDialog func(wnd uintptr)) (unhook conte
}, nil }, nil
} }
func hookDialogTitle(ctx context.Context, title string) (unhook context.CancelFunc, err error) { func hookDialogTitle(ctx context.Context, title *string) (unhook context.CancelFunc, err error) {
return hookDialog(ctx, func(wnd uintptr) { var init func(wnd uintptr)
ptr := syscall.StringToUTF16Ptr(title) if title != nil {
init = func(wnd uintptr) {
ptr := syscall.StringToUTF16Ptr(*title)
setWindowText.Call(wnd, uintptr(unsafe.Pointer(ptr))) setWindowText.Call(wnd, uintptr(unsafe.Pointer(ptr)))
}) }
}
return hookDialog(ctx, init)
} }
type _COMObject struct{} type _COMObject struct{}

View file

@ -15,13 +15,15 @@ import (
"image/color" "image/color"
) )
type constError string type stringErr string
func (e constError) Error() string { return string(e) } func (e stringErr) Error() string { return string(e) }
func stringPtr(s string) *string { return &s }
type options struct { type options struct {
// General options // General options
title string title *string
width uint width uint
height uint height uint
@ -39,9 +41,9 @@ type options struct {
// Message options // Message options
icon DialogIcon icon DialogIcon
okLabel string okLabel *string
cancelLabel string cancelLabel *string
extraButton string extraButton *string
noWrap bool noWrap bool
ellipsize bool ellipsize bool
defaultCancel bool defaultCancel bool
@ -69,7 +71,7 @@ func applyOptions(options []Option) (res options) {
// Title returns an Option to set the dialog title. // Title returns an Option to set the dialog title.
func Title(title string) Option { func Title(title string) Option {
return funcOption(func(o *options) { o.title = title }) return funcOption(func(o *options) { o.title = &title })
} }
// Width returns an Option to set the dialog width (Unix only). // Width returns an Option to set the dialog width (Unix only).
@ -95,6 +97,7 @@ const (
WarningIcon WarningIcon
InfoIcon InfoIcon
QuestionIcon QuestionIcon
NoIcon
) )
// Icon returns an Option to set the dialog icon. // Icon returns an Option to set the dialog icon.