Improve compatibility with zenity.

This commit is contained in:
Nuno Cruces 2022-11-22 18:08:35 +00:00
parent c2da2ba968
commit 56b9055f9e
5 changed files with 143 additions and 91 deletions

View file

@ -71,6 +71,8 @@ var (
// List options // List options
columns int columns int
checklist bool
radiolist bool
allowEmpty bool allowEmpty bool
// Calendar options // Calendar options
@ -92,11 +94,12 @@ var (
showPalette bool showPalette bool
// Progress options // Progress options
percentage float64 percentage float64
pulsate bool pulsate bool
autoClose bool autoClose bool
autoKill bool autoKill bool
noCancel bool noCancel bool
timeRemaining bool
// Notify options // Notify options
listen bool listen bool
@ -110,19 +113,11 @@ var (
version bool version bool
) )
func init() {
usage := flag.Usage
flag.Usage = func() {
usage()
os.Exit(-1)
}
}
func main() { func main() {
setupFlags() args := parseFlags()
flag.Parse()
validateFlags()
opts := loadFlags() opts := loadFlags()
validateFlags()
zenutil.Command = true zenutil.Command = true
zenutil.DateUTS35 = func() (string, error) { return strftime.UTS35(zenutil.DateFormat) } zenutil.DateUTS35 = func() (string, error) { return strftime.UTS35(zenutil.DateFormat) }
zenutil.DateParse = func(s string) (time.Time, error) { return strftime.Parse(zenutil.DateFormat, s) } zenutil.DateParse = func(s string) (time.Time, error) { return strftime.Parse(zenutil.DateFormat, s) }
@ -153,10 +148,19 @@ func main() {
strResult(zenity.Entry(text, opts...)) strResult(zenity.Entry(text, opts...))
case listDlg: case listDlg:
if columns > 1 {
var n int
for i := 1; i < len(args); {
args[n] = args[i]
i += columns
n += 1
}
args = args[:n:n]
}
if multiple { if multiple {
lstResult(zenity.ListMultiple(text, flag.Args(), opts...)) lstResult(zenity.ListMultiple(text, args, opts...))
} else { } else {
strResult(zenity.List(text, flag.Args(), opts...)) strResult(zenity.List(text, args, opts...))
} }
case calendarDlg: case calendarDlg:
@ -185,97 +189,102 @@ func main() {
errResult(notify(opts...)) errResult(notify(opts...))
default: default:
flag.Usage() panic("unreachable")
} }
} }
func setupFlags() { func parseFlags() []string {
fset := flag.NewFlagSet("zenity", flag.ContinueOnError)
// Application Options // Application Options
flag.BoolVar(&errorDlg, "error", false, "Display error dialog") fset.BoolVar(&errorDlg, "error", false, "Display error dialog")
flag.BoolVar(&infoDlg, "info", false, "Display info dialog") fset.BoolVar(&infoDlg, "info", false, "Display info dialog")
flag.BoolVar(&warningDlg, "warning", false, "Display warning dialog") fset.BoolVar(&warningDlg, "warning", false, "Display warning dialog")
flag.BoolVar(&questionDlg, "question", false, "Display question dialog") fset.BoolVar(&questionDlg, "question", false, "Display question dialog")
flag.BoolVar(&entryDlg, "entry", false, "Display text entry dialog") fset.BoolVar(&entryDlg, "entry", false, "Display text entry dialog")
flag.BoolVar(&listDlg, "list", false, "Display list dialog") fset.BoolVar(&listDlg, "list", false, "Display list dialog")
flag.BoolVar(&calendarDlg, "calendar", false, "Display calendar dialog") fset.BoolVar(&calendarDlg, "calendar", false, "Display calendar dialog")
flag.BoolVar(&passwordDlg, "password", false, "Display password dialog") fset.BoolVar(&passwordDlg, "password", false, "Display password dialog")
flag.BoolVar(&fileSelectionDlg, "file-selection", false, "Display file selection dialog") fset.BoolVar(&fileSelectionDlg, "file-selection", false, "Display file selection dialog")
flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog") fset.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog")
flag.BoolVar(&progressDlg, "progress", false, "Display progress indication dialog") fset.BoolVar(&progressDlg, "progress", false, "Display progress indication dialog")
flag.BoolVar(&notification, "notification", false, "Display notification") fset.BoolVar(&notification, "notification", false, "Display notification")
// General options // General options
flag.StringVar(&title, "title", "", "Set the dialog `title`") fset.StringVar(&title, "title", "", "Set the dialog `title`")
flag.UintVar(&width, "width", 0, "Set the `width`") fset.UintVar(&width, "width", 0, "Set the `width` (Unix only)")
flag.UintVar(&height, "height", 0, "Set the `height`") fset.UintVar(&height, "height", 0, "Set the `height` (Unix only)")
flag.StringVar(&okLabel, "ok-label", "", "Set the `label` of the OK button") fset.StringVar(&okLabel, "ok-label", "", "Set the `label` of the OK button")
flag.StringVar(&cancelLabel, "cancel-label", "", "Set the `label` of the Cancel button") fset.StringVar(&cancelLabel, "cancel-label", "", "Set the `label` of the Cancel button")
flag.Func("extra-button", "Add an extra `button`", setExtraButton) fset.Func("extra-button", "Add an extra `button`", setExtraButton)
flag.StringVar(&text, "text", "", "Set the dialog `text`") fset.StringVar(&text, "text", "", "Set the dialog `text`")
flag.StringVar(&windowIcon, "window-icon", "", "Set the window `icon` (error, info, question, warning)") fset.StringVar(&windowIcon, "window-icon", "", "Set the window `icon` (error, info, question, warning)")
flag.StringVar(&attach, "attach", "", "Set the parent `window` to attach to") fset.StringVar(&attach, "attach", "", "Set the parent `window` to attach to")
flag.BoolVar(&modal, "modal", runtime.GOOS == "darwin", "Set the modal hint") fset.BoolVar(&modal, "modal", runtime.GOOS == "darwin", "Set the modal hint")
flag.BoolVar(&multiple, "multiple", false, "Allow multiple items to be selected") fset.BoolVar(&multiple, "multiple", false, "Allow multiple items to be selected")
flag.BoolVar(&defaultCancel, "default-cancel", false, "Give Cancel button focus by default") fset.BoolVar(&defaultCancel, "default-cancel", false, "Give Cancel button focus by default")
// Message options // Message options
flag.StringVar(&icon, "icon-name", "", "Set the dialog `icon` (dialog-error, dialog-information, dialog-question, dialog-warning)") fset.StringVar(&icon, "icon-name", "", "Set the dialog `icon` (dialog-error, dialog-information, dialog-question, dialog-warning)")
flag.BoolVar(&noWrap, "no-wrap", false, "Do not enable text wrapping") fset.BoolVar(&noWrap, "no-wrap", false, "Do not enable text wrapping (Unix only)")
flag.BoolVar(&noMarkup, "no-markup", false, "Do not enable Pango markup") fset.BoolVar(&noMarkup, "no-markup", false, "Do not enable Pango markup")
flag.BoolVar(&ellipsize, "ellipsize", false, "Enable ellipsizing in the dialog text") fset.BoolVar(&ellipsize, "ellipsize", false, "Enable ellipsizing in the dialog text (Unix only)")
// Entry options // Entry options
flag.StringVar(&entryText, "entry-text", "", "Set the entry `text`") fset.StringVar(&entryText, "entry-text", "", "Set the entry `text`")
flag.BoolVar(&hideText, "hide-text", false, "Hide the entry text") fset.BoolVar(&hideText, "hide-text", false, "Hide the entry text")
// Password options // Password options
flag.BoolVar(&username, "username", false, "Display the username option") fset.BoolVar(&username, "username", false, "Display the username option")
// List options // List options
flag.Func("column", "Set the column `header`", addColumn) fset.Func("column", "Set the column `header`", addColumn)
flag.Bool("hide-header", true, "Hide the column headers") fset.Bool("hide-header", true, "Hide the column headers")
flag.BoolVar(&allowEmpty, "allow-empty", true, "Allow empty selection (macOS only)") fset.BoolVar(&checklist, "checklist", false, "Use check boxes for the first column (Unix only)")
fset.BoolVar(&radiolist, "radiolist", false, "Use radio buttons for the first column (Unix only)")
fset.BoolVar(&allowEmpty, "allow-empty", true, "Allow empty selection (macOS only)")
// Calendar options // Calendar options
flag.UintVar(&year, "year", 0, "Set the calendar `year`") fset.UintVar(&year, "year", 0, "Set the calendar `year`")
flag.UintVar(&month, "month", 0, "Set the calendar `month`") fset.UintVar(&month, "month", 0, "Set the calendar `month`")
flag.UintVar(&day, "day", 0, "Set the calendar `day`") fset.UintVar(&day, "day", 0, "Set the calendar `day`")
flag.StringVar(&zenutil.DateFormat, "date-format", "%m/%d/%Y", "Set the `format` for the returned date") fset.StringVar(&zenutil.DateFormat, "date-format", "%m/%d/%Y", "Set the `format` for the returned date")
// File selection options // File selection options
flag.BoolVar(&save, "save", false, "Activate save mode") fset.BoolVar(&save, "save", false, "Activate save mode")
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection") fset.BoolVar(&directory, "directory", false, "Activate directory-only selection")
flag.BoolVar(&confirmOverwrite, "confirm-overwrite", false, "Confirm file selection if filename already exists") fset.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)") fset.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)") fset.BoolVar(&showHidden, "show-hidden", false, "Show hidden files (Windows and macOS only)")
flag.StringVar(&filename, "filename", "", "Set the `filename`") fset.StringVar(&filename, "filename", "", "Set the `filename`")
flag.Func("file-filter", "Set a filename `filter` (NAME | PATTERN1 PATTERN2 ...)", addFileFilter) fset.Func("file-filter", "Set a filename `filter` (NAME | PATTERN1 PATTERN2 ...)", addFileFilter)
// Color selection options // Color selection options
flag.StringVar(&defaultColor, "color", "", "Set the `color`") fset.StringVar(&defaultColor, "color", "", "Set the `color`")
flag.BoolVar(&showPalette, "show-palette", false, "Show the palette") fset.BoolVar(&showPalette, "show-palette", false, "Show the palette")
// Progress options // Progress options
flag.Float64Var(&percentage, "percentage", 0, "Set initial `percentage`") fset.Float64Var(&percentage, "percentage", 0, "Set initial `percentage`")
flag.BoolVar(&pulsate, "pulsate", false, "Pulsate progress bar") fset.BoolVar(&pulsate, "pulsate", false, "Pulsate progress bar")
flag.BoolVar(&noCancel, "no-cancel", false, "Hide Cancel button (Windows and Unix only)") fset.BoolVar(&autoClose, "auto-close", false, "Dismiss the dialog when 100% has been reached")
flag.BoolVar(&autoClose, "auto-close", false, "Dismiss the dialog when 100% has been reached") fset.BoolVar(&autoKill, "auto-kill", false, "Kill parent process if Cancel button is pressed (macOS and Unix only)")
flag.BoolVar(&autoKill, "auto-kill", false, "Kill parent process if Cancel button is pressed (macOS and Unix only)") fset.BoolVar(&noCancel, "no-cancel", false, "Hide Cancel button (Windows and Unix only)")
fset.BoolVar(&timeRemaining, "time-remaining", false, "Estimate when progress will reach 100% (Unix only)")
// Notify options // Notify options
flag.BoolVar(&listen, "listen", false, "Listen for commands on stdin") fset.BoolVar(&listen, "listen", false, "Listen for commands on stdin")
// Windows specific options // Windows specific options
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
flag.BoolVar(&unixeol, "unixeol", false, "Use Unix line endings (Windows only)") fset.BoolVar(&unixeol, "unixeol", false, "Use Unix line endings (Windows only)")
flag.BoolVar(&cygpath, "cygpath", false, "Use cygpath for path translation (Windows only)") fset.BoolVar(&cygpath, "cygpath", false, "Use cygpath for path translation (Windows only)")
flag.BoolVar(&wslpath, "wslpath", false, "Use wslpath for path translation (Windows only)") fset.BoolVar(&wslpath, "wslpath", false, "Use wslpath for path translation (Windows only)")
} }
// Command options // Command options
flag.BoolVar(&version, "version", false, "Show version of program") fset.BoolVar(&version, "version", false, "Show version of program")
flag.IntVar(&zenutil.Timeout, "timeout", 0, "Set dialog `timeout` in seconds") fset.IntVar(&zenutil.Timeout, "timeout", 0, "Set dialog `timeout` in seconds")
flag.StringVar(&zenutil.Separator, "separator", "|", "Set output `separator` character") fset.StringVar(&zenutil.Separator, "separator", "|", "Set output `separator` character")
// Detect unspecified values // Detect unspecified values
title = unspecified title = unspecified
@ -285,15 +294,28 @@ func setupFlags() {
text = unspecified text = unspecified
icon = unspecified icon = unspecified
windowIcon = unspecified windowIcon = unspecified
fset.Usage = func() {}
err := fset.Parse(os.Args[1:])
if err == flag.ErrHelp {
fmt.Println("usage: zenity [options...]")
fset.PrintDefaults()
os.Exit(0)
}
if err != nil {
os.Exit(-1)
}
return fset.Args()
} }
func validateFlags() { func validateFlags() {
var n int
if version { if version {
fmt.Printf("zenity %s %s/%s\n", getVersion(), runtime.GOOS, runtime.GOARCH) fmt.Printf("zenity %s %s/%s\n", getVersion(), runtime.GOOS, runtime.GOARCH)
fmt.Println("https://github.com/ncruces/zenity") fmt.Println("https://github.com/ncruces/zenity")
os.Exit(0) os.Exit(0)
} }
var n int
if errorDlg { if errorDlg {
n++ n++
} }
@ -330,8 +352,28 @@ func validateFlags() {
if notification { if notification {
n++ n++
} }
if n != 1 { if n == 0 {
flag.Usage() os.Stderr.WriteString("no dialog type specified; try 'zenity --help'\n")
os.Exit(-1)
}
if n >= 2 {
os.Stderr.WriteString("two or more dialogs types specified\n")
os.Exit(-1)
}
if checklist {
multiple = true
}
if radiolist {
multiple = false
}
if checklist && radiolist {
os.Stderr.WriteString("two or more list dialog types specified\n")
os.Exit(-1)
}
if !checklist && !radiolist && columns > 1 || columns > 2 {
os.Stderr.WriteString("multiple columns not supported\n")
os.Exit(-1)
} }
} }
@ -468,7 +510,7 @@ func loadFlags() []zenity.Option {
if ellipsize { if ellipsize {
opts = append(opts, zenity.Ellipsize()) opts = append(opts, zenity.Ellipsize())
} }
if noMarkup == false { if !noMarkup {
switch { switch {
case errorDlg, infoDlg, warningDlg, questionDlg: case errorDlg, infoDlg, warningDlg, questionDlg:
text = zencmd.StripMarkup(text) text = zencmd.StripMarkup(text)
@ -490,10 +532,18 @@ func loadFlags() []zenity.Option {
// List options // List options
if checklist {
opts = append(opts, zenity.CheckList())
}
if radiolist {
opts = append(opts, zenity.RadioList())
}
if !allowEmpty { if !allowEmpty {
opts = append(opts, zenity.DisallowEmpty()) opts = append(opts, zenity.DisallowEmpty())
} }
// Calendar options
y, m, d := time.Now().Date() y, m, d := time.Now().Date()
if month != 0 { if month != 0 {
m = time.Month(month) m = time.Month(month)
@ -542,6 +592,9 @@ func loadFlags() []zenity.Option {
if noCancel { if noCancel {
opts = append(opts, zenity.NoCancel()) opts = append(opts, zenity.NoCancel())
} }
if timeRemaining {
opts = append(opts, zenity.TimeRemaining())
}
return opts return opts
} }
@ -665,9 +718,6 @@ func setExtraButton(s string) error {
func addColumn(s string) error { func addColumn(s string) error {
columns++ columns++
if columns > 1 {
return errors.New("multiple columns not supported")
}
return nil return nil
} }

View file

@ -30,7 +30,8 @@ func notify(opts ...zenity.Option) error {
cmd = strings.TrimSpace(line[:n]) cmd = strings.TrimSpace(line[:n])
msg = strings.TrimSpace(zencmd.Unescape(line[n+1:])) msg = strings.TrimSpace(zencmd.Unescape(line[n+1:]))
} else { } else {
fmt.Fprint(os.Stderr, "Could not parse command from stdin") os.Stderr.WriteString("Could not parse command from stdin\n")
continue
} }
switch cmd { switch cmd {
case "icon": case "icon":
@ -60,7 +61,7 @@ func notify(opts ...zenity.Option) error {
case "visible", "hints": case "visible", "hints":
// ignored // ignored
default: default:
fmt.Fprintf(os.Stderr, "Unknown command %q", cmd) fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
} }
} }
return nil return nil

View file

@ -68,6 +68,7 @@ const (
// Window classes // Window classes
PROGRESS_CLASS = "msctls_progress32" PROGRESS_CLASS = "msctls_progress32"
MONTHCAL_CLASS = "SysMonthCal32" MONTHCAL_CLASS = "SysMonthCal32"
WC_LISTVIEW = "SysListView32"
// Window styles // Window styles
WS_OVERLAPPED = 0x00000000 WS_OVERLAPPED = 0x00000000

View file

@ -13,7 +13,7 @@ func list(text string, items []string, opts options) (string, error) {
if opts.listKind == radioListKind { if opts.listKind == radioListKind {
args = append(args, "--radiolist", "--column=", "--column=") args = append(args, "--radiolist", "--column=", "--column=")
for _, i := range items { for _, i := range items {
args = append(args, i, i) args = append(args, "", i)
} }
} else { } else {
args = append(args, "--column=") args = append(args, "--column=")
@ -33,7 +33,7 @@ func listMultiple(text string, items []string, opts options) ([]string, error) {
if opts.listKind == checkListKind { if opts.listKind == checkListKind {
args = append(args, "--checklist", "--column=", "--column=") args = append(args, "--checklist", "--column=", "--column=")
for _, i := range items { for _, i := range items {
args = append(args, i, i) args = append(args, "", i)
} }
} else { } else {
args = append(args, "--column=") args = append(args, "--column=")

View file

@ -31,7 +31,7 @@ type ProgressDialog interface {
Done() <-chan struct{} Done() <-chan struct{}
} }
// MaxValue returns an Option to set the maximum value (Windows and macOS only). // MaxValue returns an Option to set the maximum value.
// The default maximum value is 100. // The default maximum value is 100.
func MaxValue(value int) Option { func MaxValue(value int) Option {
return funcOption(func(o *options) { o.maxValue = value }) return funcOption(func(o *options) { o.maxValue = value })