diff --git a/cmd/zenity/main.go b/cmd/zenity/main.go index cb5b8ee..395e5b3 100644 --- a/cmd/zenity/main.go +++ b/cmd/zenity/main.go @@ -17,6 +17,10 @@ import ( //go:generate go run github.com/josephspurrier/goversioninfo/cmd/goversioninfo -platform-specific -manifest=win.manifest +const ( + unspecified = "\x00" +) + var ( // Application Options notification bool @@ -122,14 +126,14 @@ func setupFlags() { flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog") // General options - flag.StringVar(&title, "title", "", "Set the dialog title") - flag.StringVar(&icon, "window-icon", "", "Set the window icon (error, info, question, warning)") - flag.UintVar(&width, "width", 0, "Set the width") - flag.UintVar(&height, "height", 0, "Set the height") + flag.StringVar(&title, "title", "", "Set the dialog `title`") + flag.StringVar(&icon, "window-icon", "", "Set the window `icon` (error, info, question, warning)") + flag.UintVar(&width, "width", 0, "Set the `width`") + flag.UintVar(&height, "height", 0, "Set the `height`") // Message options - 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(&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(&okLabel, "ok-label", "", "Set the label of the OK button") flag.StringVar(&cancelLabel, "cancel-label", "", "Set the label of the Cancel 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(&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.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 ...)") // 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") // Windows specific options @@ -157,9 +161,17 @@ func setupFlags() { flag.BoolVar(&wslpath, "wslpath", false, "Use wslpath for path translation (Windows only)") } - // Internal options - flag.IntVar(&zenutil.Timeout, "timeout", 0, "Set dialog timeout in seconds") - flag.StringVar(&zenutil.Separator, "separator", "|", "Set output separator character") + // Command options + flag.IntVar(&zenutil.Timeout, "timeout", 0, "Set dialog `timeout` in seconds") + 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() { @@ -193,25 +205,45 @@ func validateFlags() { func loadFlags() []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.") + case infoDlg: + setDefault(&title, "Information") + setDefault(&icon, "dialog-information") + setDefault(&text, "All updates are complete.") + case warningDlg: + setDefault(&title, "Warning") + setDefault(&icon, "dialog-warning") + setDefault(&text, "Are you sure you want to proceed?") + case questionDlg: + setDefault(&title, "Question") + setDefault(&icon, "dialog-question") + setDefault(&text, "Are you sure you want to proceed?") + default: + setDefault(&text, "") + } + // General options - opts = append(opts, zenity.Title(title)) + if title != unspecified { + opts = append(opts, zenity.Title(title)) + } opts = append(opts, zenity.Width(width)) opts = append(opts, zenity.Height(height)) // Message options 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 { case "error", "dialog-error": ico = zenity.ErrorIcon @@ -224,9 +256,15 @@ func loadFlags() []zenity.Option { } opts = append(opts, zenity.Icon(ico)) - opts = append(opts, zenity.OKLabel(okLabel)) - opts = append(opts, zenity.CancelLabel(cancelLabel)) - opts = append(opts, zenity.ExtraButton(extraButton)) + if okLabel != unspecified { + opts = append(opts, zenity.OKLabel(okLabel)) + } + if cancelLabel != unspecified { + opts = append(opts, zenity.CancelLabel(cancelLabel)) + } + if extraButton != unspecified { + opts = append(opts, zenity.ExtraButton(extraButton)) + } if noWrap { opts = append(opts, zenity.NoWrap()) } diff --git a/internal/zenutil/osa_generated.go b/internal/zenutil/osa_generated.go index 2c7dc4d..555a84b 100644 --- a/internal/zenutil/osa_generated.go +++ b/internal/zenutil/osa_generated.go @@ -28,10 +28,13 @@ if(Array.isArray(res)){res.join({{json .Separator}})}else{res.toString()} var app=Application.currentApplication() app.includeStandardAdditions=true app.activate() +ObjC.import("stdlib") +ObjC.import("stdio") var res=app.{{.Operation}}({{json .Text}},{{json .Options}}) -if(res.gaveUp){ObjC.import("stdlib") -$.exit(5)} -if(res.buttonReturned==={{json .Extra}}){res.buttonReturned}else{void 0} +if(res.gaveUp){$.exit(5)} +if(res.buttonReturned==={{json .Extra}}){$.puts(res.buttonReturned) +$.exit(1)} +void 0 {{- end}} {{define "notify" -}} var app=Application.currentApplication() diff --git a/internal/zenutil/osascripts/msg.gojs b/internal/zenutil/osascripts/msg.gojs index 428c554..a327333 100644 --- a/internal/zenutil/osascripts/msg.gojs +++ b/internal/zenutil/osascripts/msg.gojs @@ -2,13 +2,15 @@ var app = Application.currentApplication() app.includeStandardAdditions = true app.activate() +ObjC.import("stdlib") +ObjC.import("stdio") + var res = app.{{.Operation}}({{json .Text}}, {{json .Options}}) if (res.gaveUp) { - ObjC.import("stdlib") $.exit(5) } if (res.buttonReturned === {{json .Extra}}) { - res.buttonReturned -} else { - void 0 -} \ No newline at end of file + $.puts(res.buttonReturned) + $.exit(1) +} +void 0 \ No newline at end of file diff --git a/internal/zenutil/run_darwin.go b/internal/zenutil/run_darwin.go index bb2fd88..3764d5a 100644 --- a/internal/zenutil/run_darwin.go +++ b/internal/zenutil/run_darwin.go @@ -56,7 +56,7 @@ type File struct { // FileOptions is internal. type FileOptions struct { - Prompt string `json:"withPrompt,omitempty"` + Prompt *string `json:"withPrompt,omitempty"` Type []string `json:"ofType,omitempty"` Name string `json:"defaultName,omitempty"` Location string `json:"defaultLocation,omitempty"` @@ -68,7 +68,7 @@ type FileOptions struct { type Msg struct { Operation string Text string - Extra string + Extra *string Options MsgOptions } @@ -76,7 +76,7 @@ type Msg struct { type MsgOptions struct { Message string `json:"message,omitempty"` As string `json:"as,omitempty"` - Title string `json:"withTitle,omitempty"` + Title *string `json:"withTitle,omitempty"` Icon string `json:"withIcon,omitempty"` Buttons []string `json:"buttons,omitempty"` Cancel int `json:"cancelButton,omitempty"` @@ -92,6 +92,6 @@ type Notify struct { // NotifyOptions is internal. type NotifyOptions struct { - Title string `json:"withTitle,omitempty"` - Subtitle string `json:"subtitle,omitempty"` + Title *string `json:"withTitle,omitempty"` + Subtitle string `json:"subtitle,omitempty"` } diff --git a/msg.go b/msg.go index 6f7884c..188d1a5 100644 --- a/msg.go +++ b/msg.go @@ -2,7 +2,7 @@ package zenity // ErrExtraButton is returned by dialog functions when the extra button is // pressed. -const ErrExtraButton = constError("Extra button pressed") +const ErrExtraButton = stringErr("Extra button pressed") // Question displays the question dialog. // @@ -55,30 +55,30 @@ const ( // OKLabel returns an Option to set the label of the OK button. 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. 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. 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 { 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 { 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 { return funcOption(func(o *options) { o.defaultCancel = true }) } diff --git a/msg_darwin.go b/msg_darwin.go index 845846c..7806366 100644 --- a/msg_darwin.go +++ b/msg_darwin.go @@ -13,7 +13,13 @@ func message(kind messageKind, text string, options []Option) (bool, error) { data.Text = text 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 { data.Operation = "displayDialog" @@ -29,9 +35,9 @@ func message(kind messageKind, text string, options []Option) (bool, error) { } } else { data.Operation = "displayAlert" - if opts.title != "" { + if opts.title != nil { + data.Text = *opts.title data.Options.Message = text - data.Text = opts.title } switch kind { @@ -44,45 +50,53 @@ func message(kind messageKind, text string, options []Option) (bool, error) { } } - if kind != questionKind { - if dialog { - opts.okLabel = "OK" + if kind == questionKind { + // alert defaults to a single button, we need two + 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") + } + // only questions have cancel + opts.cancelLabel = nil } - if opts.okLabel != "" || opts.cancelLabel != "" || opts.extraButton != "" { - if opts.okLabel == "" { - 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 opts.extraButton == "" { - data.Options.Buttons = []string{opts.cancelLabel, opts.okLabel} + if opts.cancelLabel == nil { + opts.cancelLabel = stringPtr("Cancel") + } + if opts.extraButton == nil { + data.Options.Buttons = []string{*opts.cancelLabel, *opts.okLabel} data.Options.Default = 2 data.Options.Cancel = 1 } 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.Cancel = 2 } } else { - if opts.extraButton == "" { - data.Options.Buttons = []string{opts.okLabel} + if opts.extraButton == nil { + data.Options.Buttons = []string{*opts.okLabel} data.Options.Default = 1 } else { - data.Options.Buttons = []string{opts.extraButton, opts.okLabel} + data.Options.Buttons = []string{*opts.extraButton, *opts.okLabel} data.Options.Default = 2 } } data.Extra = opts.extraButton } - if opts.defaultCancel { + + if kind == questionKind && opts.defaultCancel { if data.Options.Cancel != 0 { data.Options.Default = data.Options.Cancel - } - if dialog && data.Options.Buttons == nil { + } else { data.Options.Default = 1 } } @@ -94,7 +108,8 @@ func message(kind messageKind, text string, options []Option) (bool, error) { if err != nil { return false, err } - if len(out) > 0 && string(out[:len(out)-1]) == opts.extraButton { + if len(out) > 0 && opts.extraButton != nil && + string(out[:len(out)-1]) == *opts.extraButton { return false, ErrExtraButton } return true, err diff --git a/zenity.go b/zenity.go index 373a643..7eaa654 100644 --- a/zenity.go +++ b/zenity.go @@ -15,13 +15,15 @@ import ( "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 { // General options - title string + title *string width uint height uint @@ -39,9 +41,9 @@ type options struct { // Message options icon DialogIcon - okLabel string - cancelLabel string - extraButton string + okLabel *string + cancelLabel *string + extraButton *string noWrap bool ellipsize bool defaultCancel bool @@ -69,7 +71,7 @@ func applyOptions(options []Option) (res options) { // Title returns an Option to set the dialog title. 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).