Calendar command.
This commit is contained in:
parent
f47c02b6d1
commit
198d2504fe
8 changed files with 233 additions and 33 deletions
|
@ -36,6 +36,7 @@ var (
|
||||||
questionDlg bool
|
questionDlg bool
|
||||||
entryDlg bool
|
entryDlg bool
|
||||||
listDlg bool
|
listDlg bool
|
||||||
|
calendarDlg bool
|
||||||
passwordDlg bool
|
passwordDlg bool
|
||||||
fileSelectionDlg bool
|
fileSelectionDlg bool
|
||||||
colorSelectionDlg bool
|
colorSelectionDlg bool
|
||||||
|
@ -66,6 +67,11 @@ var (
|
||||||
columns int
|
columns int
|
||||||
allowEmpty bool
|
allowEmpty bool
|
||||||
|
|
||||||
|
// Calendar options
|
||||||
|
year uint
|
||||||
|
month uint
|
||||||
|
day uint
|
||||||
|
|
||||||
// File selection options
|
// File selection options
|
||||||
save bool
|
save bool
|
||||||
directory bool
|
directory bool
|
||||||
|
@ -145,6 +151,9 @@ func main() {
|
||||||
strResult(zenity.List(text, flag.Args(), opts...))
|
strResult(zenity.List(text, flag.Args(), opts...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case calendarDlg:
|
||||||
|
calResult(zenity.Calendar(text, opts...))
|
||||||
|
|
||||||
case passwordDlg:
|
case passwordDlg:
|
||||||
_, pw, err := zenity.Password(opts...)
|
_, pw, err := zenity.Password(opts...)
|
||||||
strResult(pw, err)
|
strResult(pw, err)
|
||||||
|
@ -181,6 +190,7 @@ func setupFlags() {
|
||||||
flag.BoolVar(&questionDlg, "question", false, "Display question dialog")
|
flag.BoolVar(&questionDlg, "question", false, "Display question dialog")
|
||||||
flag.BoolVar(&entryDlg, "entry", false, "Display text entry dialog")
|
flag.BoolVar(&entryDlg, "entry", false, "Display text entry dialog")
|
||||||
flag.BoolVar(&listDlg, "list", false, "Display list dialog")
|
flag.BoolVar(&listDlg, "list", false, "Display list dialog")
|
||||||
|
flag.BoolVar(&calendarDlg, "calendar", false, "Display calendar dialog")
|
||||||
flag.BoolVar(&passwordDlg, "password", false, "Display password dialog")
|
flag.BoolVar(&passwordDlg, "password", false, "Display password dialog")
|
||||||
flag.BoolVar(&fileSelectionDlg, "file-selection", false, "Display file selection dialog")
|
flag.BoolVar(&fileSelectionDlg, "file-selection", false, "Display file selection dialog")
|
||||||
flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog")
|
flag.BoolVar(&colorSelectionDlg, "color-selection", false, "Display color selection dialog")
|
||||||
|
@ -214,6 +224,12 @@ func setupFlags() {
|
||||||
flag.Bool("hide-header", true, "Hide the column headers")
|
flag.Bool("hide-header", true, "Hide the column headers")
|
||||||
flag.BoolVar(&allowEmpty, "allow-empty", true, "Allow empty selection (macOS only)")
|
flag.BoolVar(&allowEmpty, "allow-empty", true, "Allow empty selection (macOS only)")
|
||||||
|
|
||||||
|
// Calendar options
|
||||||
|
flag.UintVar(&year, "year", 0, "Set the calendar `year`")
|
||||||
|
flag.UintVar(&month, "month", 0, "Set the calendar `month`")
|
||||||
|
flag.UintVar(&day, "day", 0, "Set the calendar `day`")
|
||||||
|
flag.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")
|
flag.BoolVar(&save, "save", false, "Activate save mode")
|
||||||
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection")
|
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection")
|
||||||
|
@ -283,6 +299,9 @@ func validateFlags() {
|
||||||
if listDlg {
|
if listDlg {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
if calendarDlg {
|
||||||
|
n++
|
||||||
|
}
|
||||||
if passwordDlg {
|
if passwordDlg {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
@ -341,13 +360,18 @@ func loadFlags() []zenity.Option {
|
||||||
setDefault(&text, "Select items from the list below:")
|
setDefault(&text, "Select items from the list below:")
|
||||||
setDefault(&okLabel, "OK")
|
setDefault(&okLabel, "OK")
|
||||||
setDefault(&cancelLabel, "Cancel")
|
setDefault(&cancelLabel, "Cancel")
|
||||||
|
case calendarDlg:
|
||||||
|
setDefault(&title, "Calendar selection")
|
||||||
|
setDefault(&text, "Select a date from below:")
|
||||||
|
setDefault(&okLabel, "OK")
|
||||||
|
setDefault(&cancelLabel, "Cancel")
|
||||||
case passwordDlg:
|
case passwordDlg:
|
||||||
setDefault(&title, "Type your password")
|
setDefault(&title, "Type your password")
|
||||||
setDefault(&okLabel, "OK")
|
setDefault(&okLabel, "OK")
|
||||||
setDefault(&cancelLabel, "Cancel")
|
setDefault(&cancelLabel, "Cancel")
|
||||||
case progressDlg:
|
case progressDlg:
|
||||||
setDefault(&title, "Progress")
|
setDefault(&title, "Progress")
|
||||||
setDefault(&text, "Running...")
|
setDefault(&text, "Running…")
|
||||||
setDefault(&okLabel, "OK")
|
setDefault(&okLabel, "OK")
|
||||||
setDefault(&cancelLabel, "Cancel")
|
setDefault(&cancelLabel, "Cancel")
|
||||||
}
|
}
|
||||||
|
@ -413,6 +437,18 @@ func loadFlags() []zenity.Option {
|
||||||
opts = append(opts, zenity.DisallowEmpty())
|
opts = append(opts, zenity.DisallowEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
y, m, d := time.Now().Date()
|
||||||
|
if month != 0 {
|
||||||
|
m = time.Month(month)
|
||||||
|
}
|
||||||
|
if day != 0 {
|
||||||
|
d = int(day)
|
||||||
|
}
|
||||||
|
if year != 0 {
|
||||||
|
y = int(year)
|
||||||
|
}
|
||||||
|
opts = append(opts, zenity.DefaultDate(y, m, d))
|
||||||
|
|
||||||
// File selection options
|
// File selection options
|
||||||
|
|
||||||
if directory {
|
if directory {
|
||||||
|
@ -484,6 +520,12 @@ func lstResult(l []string, err error) {
|
||||||
os.Stdout.WriteString(zenutil.LineBreak)
|
os.Stdout.WriteString(zenutil.LineBreak)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calResult(d time.Time, err error) {
|
||||||
|
errResult(err)
|
||||||
|
os.Stdout.WriteString(d.Format(zenutil.Strftime(zenutil.DateFormat)))
|
||||||
|
os.Stdout.WriteString(zenutil.LineBreak)
|
||||||
|
}
|
||||||
|
|
||||||
func colResult(c color.Color, err error) {
|
func colResult(c color.Color, err error) {
|
||||||
errResult(err)
|
errResult(err)
|
||||||
os.Stdout.WriteString(zenutil.UnparseColor(c))
|
os.Stdout.WriteString(zenutil.UnparseColor(c))
|
||||||
|
|
|
@ -10,7 +10,7 @@ func calendar(text string, opts options) (time.Time, error) {
|
||||||
var date zenutil.Date
|
var date zenutil.Date
|
||||||
|
|
||||||
date.OK, date.Cancel, date.Extra = getAlertButtons(opts)
|
date.OK, date.Cancel, date.Extra = getAlertButtons(opts)
|
||||||
date.Format = "yyyy-MM-dd"
|
date.Format = zenutil.StrftimeUTS35(zenutil.DateFormat)
|
||||||
if opts.time != nil {
|
if opts.time != nil {
|
||||||
date.Date = opts.time.Unix()
|
date.Date = opts.time.Unix()
|
||||||
}
|
}
|
||||||
|
@ -27,5 +27,5 @@ func calendar(text string, opts options) (time.Time, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, err
|
return time.Time{}, err
|
||||||
}
|
}
|
||||||
return time.Parse("2006-01-02", str)
|
return time.Parse(zenutil.Strftime(zenutil.DateFormat), str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func calendar(text string, opts options) (time.Time, error) {
|
func calendar(text string, opts options) (time.Time, error) {
|
||||||
args := []string{"--calendar", "--text", text, "--date-format=%F"}
|
args := []string{"--calendar", "--text", text, "--date-format", zenutil.DateFormat}
|
||||||
args = appendTitle(args, opts)
|
args = appendTitle(args, opts)
|
||||||
args = appendButtons(args, opts)
|
args = appendButtons(args, opts)
|
||||||
args = appendWidthHeight(args, opts)
|
args = appendWidthHeight(args, opts)
|
||||||
|
@ -27,5 +27,5 @@ func calendar(text string, opts options) (time.Time, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, err
|
return time.Time{}, err
|
||||||
}
|
}
|
||||||
return time.Parse("2006-01-02", str)
|
return time.Parse(zenutil.Strftime(zenutil.DateFormat), str)
|
||||||
}
|
}
|
||||||
|
|
151
internal/zenutil/date.go
Normal file
151
internal/zenutil/date.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package zenutil
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// https://strftime.org/
|
||||||
|
var strftimeTable = map[byte]string{
|
||||||
|
'B': "January",
|
||||||
|
'b': "Jan",
|
||||||
|
'h': "Jan",
|
||||||
|
'm': "01",
|
||||||
|
'A': "Monday",
|
||||||
|
'a': "Mon",
|
||||||
|
'e': "_2",
|
||||||
|
'd': "02",
|
||||||
|
'j': "002",
|
||||||
|
'H': "15",
|
||||||
|
'I': "03",
|
||||||
|
'M': "04",
|
||||||
|
'S': "05",
|
||||||
|
'Y': "2006",
|
||||||
|
'y': "06",
|
||||||
|
'p': "PM",
|
||||||
|
'Z': "MST",
|
||||||
|
'z': "-0700",
|
||||||
|
'L': "000",
|
||||||
|
'f': "000000",
|
||||||
|
|
||||||
|
'+': "Mon Jan _2 03:04:05 PM MST 2006",
|
||||||
|
'c': "Mon Jan _2 15:04:05 2006",
|
||||||
|
'F': "2006-01-02",
|
||||||
|
'D': "01/02/06",
|
||||||
|
'x': "01/02/06",
|
||||||
|
'r': "03:04:05 PM",
|
||||||
|
'T': "15:04:05",
|
||||||
|
'X': "15:04:05",
|
||||||
|
'R': "15:04",
|
||||||
|
|
||||||
|
'%': "%",
|
||||||
|
't': "\t",
|
||||||
|
'n': LineBreak,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strftime is internal.
|
||||||
|
func Strftime(fmt string) string {
|
||||||
|
var res strings.Builder
|
||||||
|
res.Grow(len(fmt))
|
||||||
|
|
||||||
|
const (
|
||||||
|
initial = iota
|
||||||
|
special
|
||||||
|
)
|
||||||
|
|
||||||
|
state := initial
|
||||||
|
for _, b := range []byte(fmt) {
|
||||||
|
switch state {
|
||||||
|
case initial:
|
||||||
|
if b == '%' {
|
||||||
|
state = special
|
||||||
|
} else {
|
||||||
|
res.WriteByte(b)
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
|
||||||
|
case special:
|
||||||
|
s, ok := strftimeTable[b]
|
||||||
|
if ok {
|
||||||
|
res.WriteString(s)
|
||||||
|
} else {
|
||||||
|
res.WriteByte(b)
|
||||||
|
}
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://nsdateformatter.com/
|
||||||
|
var strftimeUTS35Table = map[byte]string{
|
||||||
|
'B': "MMMM",
|
||||||
|
'b': "MMM",
|
||||||
|
'h': "MMM",
|
||||||
|
'm': "MM",
|
||||||
|
'A': "EEEE",
|
||||||
|
'a': "E",
|
||||||
|
'd': "dd",
|
||||||
|
'j': "DDD",
|
||||||
|
'H': "HH",
|
||||||
|
'I': "hh",
|
||||||
|
'M': "mm",
|
||||||
|
'S': "ss",
|
||||||
|
'Y': "yyyy",
|
||||||
|
'y': "yy",
|
||||||
|
'G': "YYYY",
|
||||||
|
'g': "YY",
|
||||||
|
'V': "ww",
|
||||||
|
'p': "a",
|
||||||
|
'Z': "zzz",
|
||||||
|
'z': "Z",
|
||||||
|
'L': "SSS",
|
||||||
|
'f': "SSSSSS",
|
||||||
|
|
||||||
|
'+': "E MMM d hh:mm:ss a zzz yyyy",
|
||||||
|
'c': "E MMM d HH:mm:ss yyyy",
|
||||||
|
'F': "yyyy-MM-dd",
|
||||||
|
'D': "MM/dd/yy",
|
||||||
|
'x': "MM/dd/yy",
|
||||||
|
'r': "hh:mm:ss a",
|
||||||
|
'T': "HH:mm:ss",
|
||||||
|
'X': "HH:mm:ss",
|
||||||
|
'R': "HH:mm",
|
||||||
|
|
||||||
|
'%': "%",
|
||||||
|
't': "\t",
|
||||||
|
'n': LineBreak,
|
||||||
|
}
|
||||||
|
|
||||||
|
// StrftimeUTS35 is internal.
|
||||||
|
func StrftimeUTS35(fmt string) string {
|
||||||
|
var res strings.Builder
|
||||||
|
res.Grow(len(fmt))
|
||||||
|
|
||||||
|
const (
|
||||||
|
initial = iota
|
||||||
|
special
|
||||||
|
)
|
||||||
|
|
||||||
|
state := initial
|
||||||
|
for _, b := range []byte(fmt) {
|
||||||
|
switch state {
|
||||||
|
case initial:
|
||||||
|
if b == '%' {
|
||||||
|
state = special
|
||||||
|
} else {
|
||||||
|
res.WriteByte(b)
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
|
||||||
|
case special:
|
||||||
|
s, ok := strftimeUTS35Table[b]
|
||||||
|
if ok {
|
||||||
|
res.WriteString(s)
|
||||||
|
} else {
|
||||||
|
res.WriteByte(b)
|
||||||
|
}
|
||||||
|
state = initial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.String()
|
||||||
|
}
|
|
@ -6,4 +6,5 @@ var (
|
||||||
Timeout int
|
Timeout int
|
||||||
LineBreak = "\n"
|
LineBreak = "\n"
|
||||||
Separator = "\x00"
|
Separator = "\x00"
|
||||||
|
DateFormat = "%F"
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,4 +9,5 @@ var (
|
||||||
Timeout int
|
Timeout int
|
||||||
LineBreak = "\n"
|
LineBreak = "\n"
|
||||||
Separator = "\x1e"
|
Separator = "\x1e"
|
||||||
|
DateFormat = "%F"
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,4 +6,5 @@ var (
|
||||||
Timeout int
|
Timeout int
|
||||||
Separator string
|
Separator string
|
||||||
LineBreak = "\r\n"
|
LineBreak = "\r\n"
|
||||||
|
DateFormat = "%F"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package zenutil
|
package zenutil
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
// Unescape is internal.
|
// Unescape is internal.
|
||||||
func Unescape(s string) string {
|
func Unescape(s string) string {
|
||||||
// Apply rules described in:
|
// Apply rules described in:
|
||||||
|
@ -12,7 +14,7 @@ func Unescape(s string) string {
|
||||||
escape3
|
escape3
|
||||||
)
|
)
|
||||||
var oct byte
|
var oct byte
|
||||||
var res []byte
|
var res strings.Builder
|
||||||
state := initial
|
state := initial
|
||||||
for _, b := range []byte(s) {
|
for _, b := range []byte(s) {
|
||||||
switch state {
|
switch state {
|
||||||
|
@ -21,7 +23,7 @@ func Unescape(s string) string {
|
||||||
case '\\':
|
case '\\':
|
||||||
state = escape1
|
state = escape1
|
||||||
default:
|
default:
|
||||||
res = append(res, b)
|
res.WriteByte(b)
|
||||||
state = initial
|
state = initial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,25 +33,25 @@ func Unescape(s string) string {
|
||||||
oct = b - '0'
|
oct = b - '0'
|
||||||
state = escape2
|
state = escape2
|
||||||
case 'b':
|
case 'b':
|
||||||
res = append(res, '\b')
|
res.WriteByte('\b')
|
||||||
state = initial
|
state = initial
|
||||||
case 'f':
|
case 'f':
|
||||||
res = append(res, '\f')
|
res.WriteByte('\f')
|
||||||
state = initial
|
state = initial
|
||||||
case 'n':
|
case 'n':
|
||||||
res = append(res, '\n')
|
res.WriteByte('\n')
|
||||||
state = initial
|
state = initial
|
||||||
case 'r':
|
case 'r':
|
||||||
res = append(res, '\r')
|
res.WriteByte('\r')
|
||||||
state = initial
|
state = initial
|
||||||
case 't':
|
case 't':
|
||||||
res = append(res, '\t')
|
res.WriteByte('\t')
|
||||||
state = initial
|
state = initial
|
||||||
case 'v':
|
case 'v':
|
||||||
res = append(res, '\v')
|
res.WriteByte('\v')
|
||||||
state = initial
|
state = initial
|
||||||
default:
|
default:
|
||||||
res = append(res, b)
|
res.WriteByte(b)
|
||||||
state = initial
|
state = initial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,10 +61,11 @@ func Unescape(s string) string {
|
||||||
oct = oct<<3 | (b - '0')
|
oct = oct<<3 | (b - '0')
|
||||||
state = escape3
|
state = escape3
|
||||||
case '\\':
|
case '\\':
|
||||||
res = append(res, oct)
|
res.WriteByte(oct)
|
||||||
state = escape1
|
state = escape1
|
||||||
default:
|
default:
|
||||||
res = append(res, oct, b)
|
res.WriteByte(oct)
|
||||||
|
res.WriteByte(b)
|
||||||
state = initial
|
state = initial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,20 +73,21 @@ func Unescape(s string) string {
|
||||||
switch b {
|
switch b {
|
||||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||||
oct = oct<<3 | (b - '0')
|
oct = oct<<3 | (b - '0')
|
||||||
res = append(res, oct)
|
res.WriteByte(oct)
|
||||||
state = initial
|
state = initial
|
||||||
case '\\':
|
case '\\':
|
||||||
res = append(res, oct)
|
res.WriteByte(oct)
|
||||||
state = escape1
|
state = escape1
|
||||||
default:
|
default:
|
||||||
res = append(res, oct, b)
|
res.WriteByte(oct)
|
||||||
|
res.WriteByte(b)
|
||||||
state = initial
|
state = initial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if state == escape2 || state == escape3 {
|
if state == escape2 || state == escape3 {
|
||||||
res = append(res, oct)
|
res.WriteByte(oct)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(res)
|
return res.String()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue