zenity/cmd/zenity/main.go

514 lines
12 KiB
Go
Raw Normal View History

2020-01-06 19:57:00 -05:00
package main
import (
2021-03-05 21:29:00 -05:00
"bytes"
2020-01-28 07:46:43 -05:00
"context"
2020-01-08 13:12:29 -05:00
"flag"
2020-01-21 11:13:45 -05:00
"image/color"
2020-01-06 19:57:00 -05:00
"os"
2020-01-17 07:28:44 -05:00
"os/exec"
"path/filepath"
"runtime"
2020-01-08 13:12:29 -05:00
"strings"
2020-01-28 07:46:43 -05:00
"time"
2020-01-08 13:12:29 -05:00
2020-01-06 19:57:00 -05:00
"github.com/ncruces/zenity"
2020-01-19 06:57:05 -05:00
"github.com/ncruces/zenity/internal/zenutil"
2020-01-06 19:57:00 -05:00
)
2021-04-05 13:44:59 -04:00
//go:generate go run github.com/josephspurrier/goversioninfo/cmd/goversioninfo
2020-01-08 19:54:34 -05:00
2021-03-03 21:52:05 -05:00
const (
unspecified = "\x00"
)
2020-01-08 13:12:29 -05:00
var (
// Application Options
2020-01-27 10:42:43 -05:00
notification bool
2021-03-04 21:01:59 -05:00
entryDlg bool
2020-01-21 11:13:45 -05:00
errorDlg bool
infoDlg bool
warningDlg bool
questionDlg bool
2021-03-05 21:07:00 -05:00
passwordDlg bool
2020-01-21 11:13:45 -05:00
fileSelectionDlg bool
colorSelectionDlg bool
2020-01-08 13:12:29 -05:00
// General options
2021-03-05 10:14:30 -05:00
title string
width uint
height uint
okLabel string
cancelLabel string
extraButton string
text string
icon string
2021-03-04 21:01:59 -05:00
// Entry options
entryText string
hideText bool
2020-01-08 13:12:29 -05:00
// Message options
noWrap bool
ellipsize bool
defaultCancel bool
// File selection options
save bool
multiple bool
directory bool
confirmOverwrite bool
confirmCreate bool
showHidden bool
2020-01-08 13:12:29 -05:00
filename string
fileFilters FileFilters
2020-01-17 07:28:44 -05:00
2020-01-21 11:13:45 -05:00
// Color selection options
2020-01-24 07:52:45 -05:00
defaultColor string
showPalette bool
2020-01-21 11:13:45 -05:00
2020-01-17 07:28:44 -05:00
// Windows specific options
cygpath bool
wslpath bool
2020-01-08 13:12:29 -05:00
)
2020-01-28 07:46:43 -05:00
func init() {
prevUsage := flag.Usage
flag.Usage = func() {
prevUsage()
os.Exit(-1)
}
}
2020-01-06 19:57:00 -05:00
func main() {
2020-01-08 13:12:29 -05:00
setupFlags()
2020-01-08 22:00:22 -05:00
flag.Parse()
2020-01-08 13:12:29 -05:00
validateFlags()
opts := loadFlags()
2020-01-19 06:57:05 -05:00
zenutil.Command = true
2020-01-28 07:46:43 -05:00
if zenutil.Timeout > 0 {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(zenutil.Timeout)*time.Second)
opts = append(opts, zenity.Context(ctx))
_ = cancel
}
2020-01-06 19:57:00 -05:00
2020-01-08 13:12:29 -05:00
switch {
2020-01-27 10:42:43 -05:00
case notification:
errResult(zenity.Notify(text, opts...))
2021-03-04 21:01:59 -05:00
case entryDlg:
2021-03-05 21:07:00 -05:00
strOKResult(zenity.Entry(text, opts...))
2021-03-04 21:01:59 -05:00
2020-01-08 13:12:29 -05:00
case errorDlg:
2020-01-30 20:31:54 -05:00
okResult(zenity.Error(text, opts...))
2020-01-08 13:12:29 -05:00
case infoDlg:
2020-01-30 20:31:54 -05:00
okResult(zenity.Info(text, opts...))
2020-01-08 13:12:29 -05:00
case warningDlg:
2020-01-30 20:31:54 -05:00
okResult(zenity.Warning(text, opts...))
2020-01-08 13:12:29 -05:00
case questionDlg:
2020-01-30 20:31:54 -05:00
okResult(zenity.Question(text, opts...))
2020-01-08 19:54:34 -05:00
2021-03-05 21:07:00 -05:00
case passwordDlg:
_, pw, ok, err := zenity.Password(opts...)
strOKResult(pw, ok, err)
2020-01-08 19:54:34 -05:00
case fileSelectionDlg:
switch {
default:
2020-01-17 07:28:44 -05:00
strResult(egestPath(zenity.SelectFile(opts...)))
2020-01-08 19:54:34 -05:00
case save:
2020-01-17 07:28:44 -05:00
strResult(egestPath(zenity.SelectFileSave(opts...)))
2020-01-08 19:54:34 -05:00
case multiple:
2020-01-27 10:42:43 -05:00
listResult(egestPaths(zenity.SelectFileMutiple(opts...)))
2020-01-08 19:54:34 -05:00
}
2020-01-21 11:13:45 -05:00
case colorSelectionDlg:
2020-01-27 10:42:43 -05:00
colorResult(zenity.SelectColor(opts...))
2020-01-08 13:12:29 -05:00
}
flag.Usage()
}
func setupFlags() {
// Application Options
2020-01-27 10:42:43 -05:00
flag.BoolVar(&notification, "notification", false, "Display notification")
2021-03-04 21:01:59 -05:00
flag.BoolVar(&entryDlg, "entry", false, "Display text entry dialog")
2020-01-08 13:12:29 -05:00
flag.BoolVar(&errorDlg, "error", false, "Display error dialog")
flag.BoolVar(&infoDlg, "info", false, "Display info dialog")
flag.BoolVar(&warningDlg, "warning", false, "Display warning dialog")
flag.BoolVar(&questionDlg, "question", false, "Display question dialog")
2021-03-05 21:07:00 -05:00
flag.BoolVar(&passwordDlg, "password", false, "Display password dialog")
2020-01-08 13:12:29 -05:00
flag.BoolVar(&fileSelectionDlg, "file-selection", false, "Display file selection dialog")
2020-01-21 11:13:45 -05:00
flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog")
2020-01-08 13:12:29 -05:00
// General options
2021-03-03 21:52:05 -05:00
flag.StringVar(&title, "title", "", "Set the dialog `title`")
flag.UintVar(&width, "width", 0, "Set the `width`")
flag.UintVar(&height, "height", 0, "Set the `height`")
2021-03-05 10:14:30 -05:00
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")
2021-03-04 21:01:59 -05:00
flag.StringVar(&text, "text", "", "Set the dialog `text`")
2021-03-05 10:14:30 -05:00
flag.StringVar(&icon, "window-icon", "", "Set the window `icon` (error, info, question, warning)")
2021-03-04 21:01:59 -05:00
// Entry options
flag.StringVar(&entryText, "entry-text", "", "Set the entry `text`")
flag.BoolVar(&hideText, "hide-text", false, "Hide the entry text")
2020-01-08 13:12:29 -05:00
// Message options
2021-03-03 21:52:05 -05:00
flag.StringVar(&icon, "icon-name", "", "Set the dialog `icon` (dialog-error, dialog-information, dialog-question, dialog-warning)")
2020-01-08 13:12:29 -05:00
flag.BoolVar(&noWrap, "no-wrap", false, "Do not enable text wrapping")
flag.BoolVar(&ellipsize, "ellipsize", false, "Enable ellipsizing in the dialog text")
flag.BoolVar(&defaultCancel, "default-cancel", false, "Give Cancel button focus by default")
// File selection options
flag.BoolVar(&save, "save", false, "Activate save mode")
flag.BoolVar(&multiple, "multiple", false, "Allow multiple files to be selected")
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection")
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)")
2021-03-03 21:52:05 -05:00
flag.StringVar(&filename, "filename", "", "Set the `filename`")
2020-01-08 13:12:29 -05:00
flag.Var(&fileFilters, "file-filter", "Set a filename filter (NAME | PATTERN1 PATTERN2 ...)")
2020-01-17 07:28:44 -05:00
2020-01-21 11:13:45 -05:00
// Color selection options
2021-03-03 21:52:05 -05:00
flag.StringVar(&defaultColor, "color", "", "Set the `color`")
2020-01-24 07:52:45 -05:00
flag.BoolVar(&showPalette, "show-palette", false, "Show the palette")
2020-01-21 11:13:45 -05:00
2020-01-17 07:28:44 -05:00
// Windows specific options
if runtime.GOOS == "windows" {
flag.BoolVar(&cygpath, "cygpath", false, "Use cygpath for path translation (Windows only)")
flag.BoolVar(&wslpath, "wslpath", false, "Use wslpath for path translation (Windows only)")
}
2020-01-28 07:46:43 -05:00
2021-03-03 21:52:05 -05:00
// 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
okLabel = unspecified
cancelLabel = unspecified
extraButton = unspecified
2021-03-05 10:14:30 -05:00
text = unspecified
icon = unspecified
2020-01-08 13:12:29 -05:00
}
func validateFlags() {
var n int
2020-01-27 10:42:43 -05:00
if notification {
n++
}
2021-03-04 21:01:59 -05:00
if entryDlg {
n++
}
2020-01-08 13:12:29 -05:00
if errorDlg {
n++
}
if infoDlg {
n++
}
if warningDlg {
n++
}
if questionDlg {
n++
}
2021-03-05 21:07:00 -05:00
if passwordDlg {
n++
}
2020-01-08 13:12:29 -05:00
if fileSelectionDlg {
n++
}
2020-01-21 11:13:45 -05:00
if colorSelectionDlg {
n++
}
2020-01-08 13:12:29 -05:00
if n != 1 {
flag.Usage()
}
}
func loadFlags() []zenity.Option {
2020-01-28 07:46:43 -05:00
var opts []zenity.Option
2020-01-08 13:12:29 -05:00
2021-03-03 21:52:05 -05:00
// Defaults
setDefault := func(s *string, val string) {
if *s == unspecified {
*s = val
}
}
switch {
2021-03-04 21:01:59 -05:00
case entryDlg:
setDefault(&title, "Add a new entry")
setDefault(&text, "Enter new text:")
setDefault(&okLabel, "OK")
setDefault(&cancelLabel, "Cancel")
2021-03-03 21:52:05 -05:00
case errorDlg:
setDefault(&title, "Error")
setDefault(&icon, "dialog-error")
setDefault(&text, "An error has occurred.")
2021-03-04 08:15:06 -05:00
setDefault(&okLabel, "OK")
2021-03-03 21:52:05 -05:00
case infoDlg:
setDefault(&title, "Information")
setDefault(&icon, "dialog-information")
setDefault(&text, "All updates are complete.")
2021-03-04 08:15:06 -05:00
setDefault(&okLabel, "OK")
2021-03-03 21:52:05 -05:00
case warningDlg:
setDefault(&title, "Warning")
setDefault(&icon, "dialog-warning")
setDefault(&text, "Are you sure you want to proceed?")
2021-03-04 08:15:06 -05:00
setDefault(&okLabel, "OK")
2021-03-03 21:52:05 -05:00
case questionDlg:
setDefault(&title, "Question")
setDefault(&icon, "dialog-question")
setDefault(&text, "Are you sure you want to proceed?")
2021-03-04 08:15:06 -05:00
setDefault(&okLabel, "Yes")
setDefault(&cancelLabel, "No")
2021-03-05 21:07:00 -05:00
case passwordDlg:
2021-03-29 14:07:44 -04:00
setDefault(&title, "Type your password")
2021-03-05 21:07:00 -05:00
setDefault(&icon, "dialog-password")
setDefault(&okLabel, "OK")
setDefault(&cancelLabel, "Cancel")
2021-03-03 21:52:05 -05:00
default:
setDefault(&text, "")
}
2020-01-08 13:12:29 -05:00
// General options
2021-03-03 21:52:05 -05:00
if title != unspecified {
opts = append(opts, zenity.Title(title))
}
2021-02-12 09:24:13 -05:00
opts = append(opts, zenity.Width(width))
opts = append(opts, zenity.Height(height))
2021-03-05 10:14:30 -05:00
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))
2021-03-04 21:01:59 -05:00
}
2020-01-08 13:12:29 -05:00
2020-01-27 10:42:43 -05:00
var ico zenity.DialogIcon
switch icon {
2020-01-08 13:12:29 -05:00
case "error", "dialog-error":
2020-01-27 10:42:43 -05:00
ico = zenity.ErrorIcon
2020-01-08 13:12:29 -05:00
case "info", "dialog-information":
2020-01-27 10:42:43 -05:00
ico = zenity.InfoIcon
2020-01-08 13:12:29 -05:00
case "question", "dialog-question":
2020-01-27 10:42:43 -05:00
ico = zenity.QuestionIcon
case "important", "warning", "dialog-warning":
ico = zenity.WarningIcon
2021-03-05 10:14:30 -05:00
case "dialog-password":
ico = zenity.PasswordIcon
case "":
ico = zenity.NoIcon
2020-01-08 13:12:29 -05:00
}
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.Icon(ico))
2021-03-05 10:14:30 -05:00
// Entry options
opts = append(opts, zenity.EntryText(entryText))
if hideText {
opts = append(opts, zenity.HideText())
2021-03-03 21:52:05 -05:00
}
2021-03-05 10:14:30 -05:00
// Message options
2020-01-08 13:12:29 -05:00
if noWrap {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.NoWrap())
2020-01-08 13:12:29 -05:00
}
if ellipsize {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.Ellipsize())
2020-01-08 13:12:29 -05:00
}
if defaultCancel {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.DefaultCancel())
2020-01-08 13:12:29 -05:00
}
2020-01-08 19:54:34 -05:00
// File selection options
2020-01-09 20:46:53 -05:00
if directory {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.Directory())
2020-01-09 20:46:53 -05:00
}
2020-01-08 19:54:34 -05:00
if confirmOverwrite {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.ConfirmOverwrite())
}
if confirmCreate {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.ConfirmCreate())
}
if showHidden {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.ShowHidden())
2020-01-08 19:54:34 -05:00
}
2021-03-05 10:14:30 -05:00
if filename != "" {
opts = append(opts, zenity.Filename(ingestPath(filename)))
}
opts = append(opts, fileFilters)
2020-01-08 19:54:34 -05:00
2020-01-21 11:13:45 -05:00
// Color selection options
2020-01-24 07:52:45 -05:00
if defaultColor != "" {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.Color(zenutil.ParseColor(defaultColor)))
2020-01-21 11:13:45 -05:00
}
2020-01-24 07:52:45 -05:00
if showPalette {
2020-01-28 07:46:43 -05:00
opts = append(opts, zenity.ShowPalette())
2020-01-21 11:13:45 -05:00
}
2020-01-28 07:46:43 -05:00
return opts
2020-01-08 13:12:29 -05:00
}
2020-01-27 10:42:43 -05:00
func errResult(err error) {
2020-01-30 20:31:54 -05:00
if os.IsTimeout(err) {
os.Exit(5)
2020-01-27 10:42:43 -05:00
}
2020-01-08 13:12:29 -05:00
if err == zenity.ErrExtraButton {
os.Stdout.WriteString(extraButton)
2020-01-19 06:57:05 -05:00
os.Stdout.WriteString(zenutil.LineBreak)
2020-01-08 13:12:29 -05:00
os.Exit(1)
}
2020-01-06 19:57:00 -05:00
if err != nil {
os.Stderr.WriteString(err.Error())
2020-01-19 06:57:05 -05:00
os.Stderr.WriteString(zenutil.LineBreak)
2020-01-08 13:12:29 -05:00
os.Exit(-1)
2020-01-06 19:57:00 -05:00
}
2020-01-30 20:31:54 -05:00
os.Exit(0)
}
func okResult(ok bool, err error) {
if err != nil {
errResult(err)
}
2020-01-08 13:12:29 -05:00
if ok {
os.Exit(0)
2020-01-06 19:57:00 -05:00
}
2020-01-08 13:12:29 -05:00
os.Exit(1)
}
2020-01-08 19:54:34 -05:00
func strResult(s string, err error) {
if err != nil {
2020-01-30 20:31:54 -05:00
errResult(err)
2020-01-08 19:54:34 -05:00
}
if s == "" {
os.Exit(1)
}
os.Stdout.WriteString(s)
2020-01-19 06:57:05 -05:00
os.Stdout.WriteString(zenutil.LineBreak)
2020-01-08 19:54:34 -05:00
os.Exit(0)
}
2020-01-27 10:42:43 -05:00
func listResult(l []string, err error) {
2020-01-08 19:54:34 -05:00
if err != nil {
2020-01-30 20:31:54 -05:00
errResult(err)
2020-01-08 19:54:34 -05:00
}
if l == nil {
os.Exit(1)
}
2021-03-05 21:07:00 -05:00
os.Stdout.WriteString(strings.Join(l, zenutil.Separator))
os.Stdout.WriteString(zenutil.LineBreak)
2020-01-08 19:54:34 -05:00
os.Exit(0)
}
2020-01-27 10:42:43 -05:00
func colorResult(c color.Color, err error) {
2020-01-21 11:13:45 -05:00
if err != nil {
2020-01-30 20:31:54 -05:00
errResult(err)
2020-01-21 11:13:45 -05:00
}
if c == nil {
os.Exit(1)
}
os.Stdout.WriteString(zenutil.UnparseColor(c))
os.Stdout.WriteString(zenutil.LineBreak)
os.Exit(0)
}
2021-03-05 21:07:00 -05:00
func strOKResult(s string, ok bool, err error) {
if err != nil {
errResult(err)
}
if !ok {
os.Exit(1)
}
os.Stdout.WriteString(s)
os.Stdout.WriteString(zenutil.LineBreak)
os.Exit(0)
}
2020-01-17 07:28:44 -05:00
func ingestPath(path string) string {
if runtime.GOOS == "windows" && path != "" {
var args []string
switch {
case wslpath:
args = []string{"wsl", "wslpath", "-m"}
case cygpath:
args = []string{"cygpath", "-C", "UTF8", "-m"}
}
if args != nil {
args = append(args, path)
out, err := exec.Command(args[0], args[1:]...).Output()
2021-03-05 21:29:00 -05:00
if err == nil {
path = string(bytes.TrimSuffix(out, []byte{'\n'}))
2020-01-17 07:28:44 -05:00
}
}
}
return path
}
func egestPath(path string, err error) (string, error) {
if runtime.GOOS == "windows" && path != "" && err == nil {
var args []string
switch {
case wslpath:
args = []string{"wsl", "wslpath", "-u"}
case cygpath:
args = []string{"cygpath", "-C", "UTF8", "-u"}
}
if args != nil {
var out []byte
args = append(args, filepath.ToSlash(path))
out, err = exec.Command(args[0], args[1:]...).Output()
2021-03-05 21:29:00 -05:00
if err == nil {
path = string(bytes.TrimSuffix(out, []byte{'\n'}))
2020-01-17 07:28:44 -05:00
}
}
}
return path, err
}
func egestPaths(paths []string, err error) ([]string, error) {
if runtime.GOOS == "windows" && err == nil && (wslpath || cygpath) {
paths = append(paths[:0:0], paths...)
for i, p := range paths {
paths[i], err = egestPath(p, nil)
if err != nil {
break
}
}
}
return paths, err
}
2020-01-29 11:45:40 -05:00
// FileFilters is internal.
2020-01-08 13:12:29 -05:00
type FileFilters struct {
2020-01-08 19:54:34 -05:00
zenity.FileFilters
2020-01-08 13:12:29 -05:00
}
2020-01-29 11:45:40 -05:00
// String is internal.
2020-01-08 13:12:29 -05:00
func (f *FileFilters) String() string {
2020-01-17 07:28:44 -05:00
return "zenity.FileFilters"
2020-01-08 13:12:29 -05:00
}
2020-01-29 11:45:40 -05:00
// Set is internal.
2020-01-08 13:12:29 -05:00
func (f *FileFilters) Set(s string) error {
var filter zenity.FileFilter
2020-01-08 21:38:10 -05:00
if split := strings.SplitN(s, "|", 2); len(split) > 1 {
2021-02-18 08:14:32 -05:00
filter.Name = strings.TrimSpace(split[0])
2020-01-08 13:12:29 -05:00
s = split[1]
}
2021-02-18 08:14:32 -05:00
filter.Patterns = strings.Split(strings.TrimSpace(s), " ")
2020-01-08 19:54:34 -05:00
f.FileFilters = append(f.FileFilters, filter)
2020-01-08 13:12:29 -05:00
return nil
2020-01-06 19:57:00 -05:00
}