WIP: zenity command (windows).
This commit is contained in:
parent
7f608be6c2
commit
1fc55eed81
13 changed files with 141 additions and 34 deletions
1
cmd/zenity/.gitignore
vendored
1
cmd/zenity/.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
zenity
|
zenity
|
||||||
|
*.syso
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"github.com/ncruces/zenity/internal/cmd"
|
"github.com/ncruces/zenity/internal/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/josephspurrier/goversioninfo/cmd/goversioninfo -platform-specific -manifest=win.manifest
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Application Options
|
// Application Options
|
||||||
errorDlg bool
|
errorDlg bool
|
||||||
|
@ -58,6 +60,18 @@ func main() {
|
||||||
msgResult(zenity.Warning(text, opts...))
|
msgResult(zenity.Warning(text, opts...))
|
||||||
case questionDlg:
|
case questionDlg:
|
||||||
msgResult(zenity.Question(text, opts...))
|
msgResult(zenity.Question(text, opts...))
|
||||||
|
|
||||||
|
case fileSelectionDlg:
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
strResult(zenity.SelectFile(opts...))
|
||||||
|
case save:
|
||||||
|
strResult(zenity.SelectFileSave(opts...))
|
||||||
|
case directory:
|
||||||
|
strResult(zenity.SelectDirectory(opts...))
|
||||||
|
case multiple:
|
||||||
|
lstResult(zenity.SelectFileMutiple(opts...))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.Usage()
|
flag.Usage()
|
||||||
|
@ -95,7 +109,7 @@ func setupFlags() {
|
||||||
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection")
|
flag.BoolVar(&directory, "directory", false, "Activate directory-only selection")
|
||||||
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.StringVar(&filename, "filename", "", "Set the filename")
|
flag.StringVar(&filename, "filename", "", "Set the filename")
|
||||||
flag.StringVar(&separator, "separator", "", "Set output separator character")
|
flag.StringVar(&separator, "separator", "|", "Set output separator character")
|
||||||
flag.Var(&fileFilters, "file-filter", "Set a filename filter (NAME | PATTERN1 PATTERN2 ...)")
|
flag.Var(&fileFilters, "file-filter", "Set a filename filter (NAME | PATTERN1 PATTERN2 ...)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +171,16 @@ func loadFlags() []zenity.Option {
|
||||||
options = append(options, zenity.DefaultCancel)
|
options = append(options, zenity.DefaultCancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File selection options
|
||||||
|
|
||||||
|
options = append(options, fileFilters.New())
|
||||||
|
options = append(options, zenity.Filename(filename))
|
||||||
|
if confirmOverwrite {
|
||||||
|
options = append(options, zenity.ConfirmOverwrite)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Separator = separator
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,8 +201,36 @@ func msgResult(ok bool, err error) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func strResult(s string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
os.Stderr.WriteString(err.Error())
|
||||||
|
os.Stderr.WriteString(cmd.LineBreak)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Stdout.WriteString(s)
|
||||||
|
os.Stdout.WriteString(cmd.LineBreak)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lstResult(l []string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
os.Stderr.WriteString(err.Error())
|
||||||
|
os.Stderr.WriteString(cmd.LineBreak)
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
os.Stdout.WriteString(strings.Join(l, separator))
|
||||||
|
os.Stdout.WriteString(cmd.LineBreak)
|
||||||
|
if l == nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
type FileFilters struct {
|
type FileFilters struct {
|
||||||
list []zenity.FileFilter
|
zenity.FileFilters
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileFilters) String() string {
|
func (f *FileFilters) String() string {
|
||||||
|
@ -193,8 +245,8 @@ func (f *FileFilters) Set(s string) error {
|
||||||
s = split[1]
|
s = split[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
filter.Exts = strings.Split(s, " ")
|
filter.Patterns = strings.Split(s, " ")
|
||||||
f.list = append(f.list, filter)
|
f.FileFilters = append(f.FileFilters, filter)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
41
cmd/zenity/versioninfo.json
Normal file
41
cmd/zenity/versioninfo.json
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"FixedFileInfo": {
|
||||||
|
"FileVersion": {
|
||||||
|
"Major": 0,
|
||||||
|
"Minor": 0,
|
||||||
|
"Patch": 0,
|
||||||
|
"Build": 0
|
||||||
|
},
|
||||||
|
"ProductVersion": {
|
||||||
|
"Major": 0,
|
||||||
|
"Minor": 0,
|
||||||
|
"Patch": 0,
|
||||||
|
"Build": 0
|
||||||
|
},
|
||||||
|
"FileFlagsMask": "3f",
|
||||||
|
"FileFlags ": "00",
|
||||||
|
"FileOS": "040004",
|
||||||
|
"FileType": "01",
|
||||||
|
"FileSubType": "00"
|
||||||
|
},
|
||||||
|
"StringFileInfo": {
|
||||||
|
"Comments": "zenity",
|
||||||
|
"CompanyName": "Nuno Cruces",
|
||||||
|
"FileDescription": "",
|
||||||
|
"FileVersion": "0.0.0.0",
|
||||||
|
"InternalName": "zenity",
|
||||||
|
"LegalCopyright": "© 2020 Nuno Cruces",
|
||||||
|
"LegalTrademarks": "",
|
||||||
|
"OriginalFilename": "zenity.exe",
|
||||||
|
"PrivateBuild": "",
|
||||||
|
"ProductName": "zenity",
|
||||||
|
"ProductVersion": "0.0.0",
|
||||||
|
"SpecialBuild": ""
|
||||||
|
},
|
||||||
|
"VarFileInfo": {
|
||||||
|
"Translation": {
|
||||||
|
"LangID": "0409",
|
||||||
|
"CharsetID": "04B0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
cmd/zenity/win.manifest
Normal file
9
cmd/zenity/win.manifest
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
</assembly>
|
|
@ -92,8 +92,8 @@ func SelectDirectory(options ...Option) (string, error) {
|
||||||
func appleFilters(filters []FileFilter) []string {
|
func appleFilters(filters []FileFilter) []string {
|
||||||
var filter []string
|
var filter []string
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
for _, e := range f.Exts {
|
for _, p := range f.Patterns {
|
||||||
filter = append(filter, strings.TrimPrefix(e, "."))
|
filter = append(filter, p) // FIXME
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filter
|
return filter
|
||||||
|
|
18
file_test.go
18
file_test.go
|
@ -6,9 +6,9 @@ const defaultPath = ""
|
||||||
|
|
||||||
func TestSelectFile(t *testing.T) {
|
func TestSelectFile(t *testing.T) {
|
||||||
res, err := SelectFile(Filename(defaultPath), FileFilters{
|
res, err := SelectFile(Filename(defaultPath), FileFilters{
|
||||||
{"Go files", []string{".go"}},
|
{"Go files", []string{"*.go"}},
|
||||||
{"Web files", []string{".html", ".js", ".css"}},
|
{"Web files", []string{"*.html", "*.js", "*.css"}},
|
||||||
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
{"Image files", []string{"*.png", "*.gif", "*.ico", "*.jpg", "*.webp"}},
|
||||||
}.New())
|
}.New())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -20,9 +20,9 @@ func TestSelectFile(t *testing.T) {
|
||||||
|
|
||||||
func TestSelectFileMutiple(t *testing.T) {
|
func TestSelectFileMutiple(t *testing.T) {
|
||||||
res, err := SelectFileMutiple(Filename(defaultPath), FileFilters{
|
res, err := SelectFileMutiple(Filename(defaultPath), FileFilters{
|
||||||
{"Go files", []string{".go"}},
|
{"Go files", []string{"*.go"}},
|
||||||
{"Web files", []string{".html", ".js", ".css"}},
|
{"Web files", []string{"*.html", "*.js", "*.css"}},
|
||||||
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
{"Image files", []string{"*.png", "*.gif", "*.ico", "*.jpg", "*.webp"}},
|
||||||
}.New())
|
}.New())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -34,9 +34,9 @@ func TestSelectFileMutiple(t *testing.T) {
|
||||||
|
|
||||||
func TestSelectFileSave(t *testing.T) {
|
func TestSelectFileSave(t *testing.T) {
|
||||||
res, err := SelectFileSave(Filename(defaultPath), ConfirmOverwrite, FileFilters{
|
res, err := SelectFileSave(Filename(defaultPath), ConfirmOverwrite, FileFilters{
|
||||||
{"Go files", []string{".go"}},
|
{"Go files", []string{"*.go"}},
|
||||||
{"Web files", []string{".html", ".js", ".css"}},
|
{"Web files", []string{"*.html", "*.js", "*.css"}},
|
||||||
{"Image files", []string{".png", ".gif", ".ico", ".jpg", ".webp"}},
|
{"Image files", []string{"*.png", "*.gif", "*.ico", "*.jpg", "*.webp"}},
|
||||||
}.New())
|
}.New())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -118,9 +118,8 @@ func zenityFilters(filters []FileFilter) []string {
|
||||||
buf.WriteString(f.Name)
|
buf.WriteString(f.Name)
|
||||||
buf.WriteRune('|')
|
buf.WriteRune('|')
|
||||||
}
|
}
|
||||||
for _, e := range f.Exts {
|
for _, p := range f.Patterns {
|
||||||
buf.WriteRune('*')
|
buf.WriteString(p)
|
||||||
buf.WriteString(e)
|
|
||||||
buf.WriteRune(' ')
|
buf.WriteRune(' ')
|
||||||
}
|
}
|
||||||
res = append(res, buf.String())
|
res = append(res, buf.String())
|
||||||
|
|
|
@ -31,7 +31,9 @@ func SelectFile(options ...Option) (string, error) {
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
||||||
}
|
}
|
||||||
|
if opts.filters != nil {
|
||||||
args.Filter = &windowsFilters(opts.filters)[0]
|
args.Filter = &windowsFilters(opts.filters)[0]
|
||||||
|
}
|
||||||
|
|
||||||
res := [32768]uint16{}
|
res := [32768]uint16{}
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
|
@ -59,7 +61,9 @@ func SelectFileMutiple(options ...Option) ([]string, error) {
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
args.InitialDir = syscall.StringToUTF16Ptr(opts.filename)
|
||||||
}
|
}
|
||||||
|
if opts.filters != nil {
|
||||||
args.Filter = &windowsFilters(opts.filters)[0]
|
args.Filter = &windowsFilters(opts.filters)[0]
|
||||||
|
}
|
||||||
|
|
||||||
res := [32768 + 1024*256]uint16{}
|
res := [32768 + 1024*256]uint16{}
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
|
@ -115,7 +119,9 @@ func SelectFileSave(options ...Option) (string, error) {
|
||||||
if opts.overwrite {
|
if opts.overwrite {
|
||||||
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
|
args.Flags |= 0x2 // OFN_OVERWRITEPROMPT
|
||||||
}
|
}
|
||||||
|
if opts.filters != nil {
|
||||||
args.Filter = &windowsFilters(opts.filters)[0]
|
args.Filter = &windowsFilters(opts.filters)[0]
|
||||||
|
}
|
||||||
|
|
||||||
res := [32768]uint16{}
|
res := [32768]uint16{}
|
||||||
args.File = &res[0]
|
args.File = &res[0]
|
||||||
|
@ -236,9 +242,8 @@ func windowsFilters(filters []FileFilter) []uint16 {
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
res = append(res, utf16.Encode([]rune(f.Name))...)
|
res = append(res, utf16.Encode([]rune(f.Name))...)
|
||||||
res = append(res, 0)
|
res = append(res, 0)
|
||||||
for _, e := range f.Exts {
|
for _, p := range f.Patterns {
|
||||||
res = append(res, uint16('*'))
|
res = append(res, utf16.Encode([]rune(p))...)
|
||||||
res = append(res, utf16.Encode([]rune(e))...)
|
|
||||||
res = append(res, uint16(';'))
|
res = append(res, uint16(';'))
|
||||||
}
|
}
|
||||||
res = append(res, 0)
|
res = append(res, 0)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
var Command bool
|
var Command bool
|
||||||
|
var Separator string
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
package osa
|
package osa
|
||||||
|
|
||||||
|
//go:generate go run scripts/generate.go scripts/
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"github.com/ncruces/zenity/internal/cmd"
|
"github.com/ncruces/zenity/internal/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run scripts/generate.go scripts/
|
|
||||||
|
|
||||||
func Run(script string, data interface{}) ([]byte, error) {
|
func Run(script string, data interface{}) ([]byte, error) {
|
||||||
var buf strings.Builder
|
var buf strings.Builder
|
||||||
|
|
||||||
|
|
|
@ -91,13 +91,12 @@ func message(typ int, text string, options []Option) (bool, error) {
|
||||||
func hookMessageLabels(typ int, opts options) (hook uintptr, err error) {
|
func hookMessageLabels(typ int, opts options) (hook uintptr, err error) {
|
||||||
tid, _, _ := getCurrentThreadId.Call()
|
tid, _, _ := getCurrentThreadId.Call()
|
||||||
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
hook, _, err = setWindowsHookEx.Call(12, // WH_CALLWNDPROCRET
|
||||||
syscall.NewCallback(func(code int, wparam, lparam uintptr) uintptr {
|
syscall.NewCallback(func(code int, wparam uintptr, lparam *_CWPRETSTRUCT) uintptr {
|
||||||
msg := *(*_CWPRETSTRUCT)(unsafe.Pointer(lparam))
|
if lparam.Message == 0x0110 { // WM_INITDIALOG
|
||||||
if msg.Message == 0x0110 { // WM_INITDIALOG
|
|
||||||
name := [7]byte{}
|
name := [7]byte{}
|
||||||
n, _, _ := getClassName.Call(msg.HWnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
n, _, _ := getClassName.Call(lparam.HWnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
||||||
if string(name[:n]) == "#32770" {
|
if string(name[:n]) == "#32770" {
|
||||||
enumChildWindows.Call(msg.HWnd,
|
enumChildWindows.Call(lparam.HWnd,
|
||||||
syscall.NewCallback(func(hwnd, lparam uintptr) uintptr {
|
syscall.NewCallback(func(hwnd, lparam uintptr) uintptr {
|
||||||
name := [7]byte{}
|
name := [7]byte{}
|
||||||
n, _, _ := getClassName.Call(hwnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
n, _, _ := getClassName.Call(hwnd, uintptr(unsafe.Pointer(&name)), uintptr(len(name)))
|
||||||
|
@ -127,7 +126,7 @@ func hookMessageLabels(typ int, opts options) (hook uintptr, err error) {
|
||||||
}), 0)
|
}), 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next, _, _ := callNextHookEx.Call(hook, uintptr(code), wparam, lparam)
|
next, _, _ := callNextHookEx.Call(hook, uintptr(code), wparam, uintptr(unsafe.Pointer(lparam)))
|
||||||
return next
|
return next
|
||||||
}), 0, tid)
|
}), 0, tid)
|
||||||
return
|
return
|
||||||
|
|
|
@ -62,7 +62,7 @@ func ConfirmOverwrite(o *options) {
|
||||||
|
|
||||||
type FileFilter struct {
|
type FileFilter struct {
|
||||||
Name string
|
Name string
|
||||||
Exts []string
|
Patterns []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileFilters []FileFilter
|
type FileFilters []FileFilter
|
||||||
|
|
Loading…
Reference in a new issue