Folder selection improvements, refactoring (windows).

This commit is contained in:
Nuno Cruces 2020-01-12 18:55:10 +00:00
parent 58dae10a83
commit f68fa5b0ba
5 changed files with 89 additions and 74 deletions

View file

@ -1,7 +1,6 @@
package zenity
import (
"fmt"
"path/filepath"
"reflect"
"runtime"
@ -13,7 +12,6 @@ import (
var (
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
commDlgExtendedError = comdlg32.NewProc("CommDlgExtendedError")
shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW")
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
@ -175,7 +173,7 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
_CLSID_FileOpenDialog, 0, 0x17, // CLSCTX_ALL
_IID_IFileOpenDialog, uintptr(unsafe.Pointer(&dialog)))
if int32(hr) < 0 {
return browseForFolder(opts.title)
return browseForFolder(opts)
}
defer dialog.Call(dialog.vtbl.Release)
@ -267,12 +265,22 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
return
}
func browseForFolder(title string) (string, []string, error) {
func browseForFolder(opts options) (string, []string, error) {
var args _BROWSEINFO
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
if title != "" {
args.Title = syscall.StringToUTF16Ptr(title)
if opts.title != "" {
args.Title = syscall.StringToUTF16Ptr(opts.title)
}
if opts.filename != "" {
ptr := syscall.StringToUTF16Ptr(opts.filename)
args.LParam = uintptr(unsafe.Pointer(ptr))
args.CallbackFunc = syscall.NewCallback(func(hwnd uintptr, msg uint32, lparam, data uintptr) uintptr {
if msg == 1 { // BFFM_INITIALIZED
sendMessage.Call(hwnd, 1024+103 /* BFFM_SETSELECTIONW */, 1 /* TRUE */, data)
}
return 0
})
}
ptr, _, _ := shBrowseForFolder.Call(uintptr(unsafe.Pointer(&args)))
@ -322,15 +330,6 @@ func windowsFilters(filters []FileFilter) []uint16 {
return res
}
func commDlgError() error {
n, _, _ := commDlgExtendedError.Call()
if n == 0 {
return nil
} else {
return fmt.Errorf("Common Dialog error: %x", n)
}
}
type _OPENFILENAME struct {
StructSize uint32
Owner uintptr
@ -378,23 +377,6 @@ var (
_CLSID_FileOpenDialog = uuid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
)
type _COMObject struct{}
func (o *_COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) {
self := uintptr(unsafe.Pointer(o))
nargs := uintptr(len(a))
switch nargs {
case 0:
return syscall.Syscall(trap, nargs+1, self, 0, 0)
case 1:
return syscall.Syscall(trap, nargs+1, self, a[0], 0)
case 2:
return syscall.Syscall(trap, nargs+1, self, a[0], a[1])
default:
panic("COM call with too many arguments.")
}
}
type _IFileOpenDialog struct {
_COMObject
vtbl *_IFileOpenDialogVtbl
@ -467,9 +449,3 @@ type _IShellItemArrayVtbl struct {
GetItemAt uintptr
EnumItems uintptr
}
type _IUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}

View file

@ -1,6 +1,10 @@
package zenity
import "syscall"
import (
"fmt"
"syscall"
"unsafe"
)
var (
comdlg32 = syscall.NewLazyDLL("comdlg32.dll")
@ -9,6 +13,8 @@ var (
shell32 = syscall.NewLazyDLL("shell32.dll")
user32 = syscall.NewLazyDLL("user32.dll")
commDlgExtendedError = comdlg32.NewProc("CommDlgExtendedError")
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
coInitializeEx = ole32.NewProc("CoInitializeEx")
@ -16,6 +22,7 @@ var (
coCreateInstance = ole32.NewProc("CoCreateInstance")
coTaskMemFree = ole32.NewProc("CoTaskMemFree")
sendMessage = user32.NewProc("SendMessageW")
getClassName = user32.NewProc("GetClassNameA")
setWindowsHookEx = user32.NewProc("SetWindowsHookExW")
unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx")
@ -25,6 +32,15 @@ var (
setWindowText = user32.NewProc("SetWindowTextW")
)
func commDlgError() error {
n, _, _ := commDlgExtendedError.Call()
if n == 0 {
return nil
} else {
return fmt.Errorf("Common Dialog error: %x", n)
}
}
type _CWPRETSTRUCT struct {
Result uintptr
LParam uintptr
@ -32,3 +48,26 @@ type _CWPRETSTRUCT struct {
Message uint32
HWnd uintptr
}
type _COMObject struct{}
func (o *_COMObject) Call(trap uintptr, a ...uintptr) (r1, r2 uintptr, lastErr error) {
self := uintptr(unsafe.Pointer(o))
nargs := uintptr(len(a))
switch nargs {
case 0:
return syscall.Syscall(trap, nargs+1, self, 0, 0)
case 1:
return syscall.Syscall(trap, nargs+1, self, a[0], 0)
case 2:
return syscall.Syscall(trap, nargs+1, self, a[0], a[1])
default:
panic("COM call with too many arguments.")
}
}
type _IUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}

View file

@ -6,7 +6,7 @@ import (
"github.com/ncruces/zenity/internal/osa"
)
func Error(text string, options ...Option) (bool, error) {
func Question(text string, options ...Option) (bool, error) {
return message(0, text, options)
}
@ -14,18 +14,18 @@ func Info(text string, options ...Option) (bool, error) {
return message(1, text, options)
}
func Question(text string, options ...Option) (bool, error) {
func Warning(text string, options ...Option) (bool, error) {
return message(2, text, options)
}
func Warning(text string, options ...Option) (bool, error) {
func Error(text string, options ...Option) (bool, error) {
return message(3, text, options)
}
func message(typ int, text string, options []Option) (bool, error) {
opts := optsParse(options)
data := osa.Msg{Text: text}
dialog := typ == 2 || opts.icon != 0
dialog := typ == 0 || opts.icon != 0
if dialog {
data.Operation = "displayDialog"
@ -47,16 +47,16 @@ func message(typ int, text string, options []Option) (bool, error) {
}
switch typ {
case 0:
data.As = "critical"
case 1:
data.As = "informational"
case 3:
case 2:
data.As = "warning"
case 3:
data.As = "critical"
}
}
if typ != 2 {
if typ != 0 {
if dialog {
opts.ok = "OK"
}
@ -69,7 +69,7 @@ func message(typ int, text string, options []Option) (bool, error) {
if opts.cancel == "" {
opts.cancel = "Cancel"
}
if typ == 2 {
if typ == 0 {
if opts.extra == "" {
data.Buttons = []string{opts.cancel, opts.ok}
data.Default = 2

View file

@ -8,24 +8,6 @@ import (
"github.com/ncruces/zenity/internal/zen"
)
// Display error dialog.
//
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Icon, OKLabel, ExtraButton, NoWrap, Ellipsize.
func Error(text string, options ...Option) (bool, error) {
return message("--error", text, options)
}
// Display info dialog.
//
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Icon, OKLabel, ExtraButton, NoWrap, Ellipsize.
func Info(text string, options ...Option) (bool, error) {
return message("--info", text, options)
}
// Display question dialog.
//
// Returns true on OK, false on Cancel, or ErrExtraButton.
@ -36,6 +18,15 @@ func Question(text string, options ...Option) (bool, error) {
return message("--question", text, options)
}
// Display info dialog.
//
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Icon, OKLabel, ExtraButton, NoWrap, Ellipsize.
func Info(text string, options ...Option) (bool, error) {
return message("--info", text, options)
}
// Display warning dialog.
//
// Returns true on OK, false on dismiss, or ErrExtraButton.
@ -45,6 +36,15 @@ func Warning(text string, options ...Option) (bool, error) {
return message("--warning", text, options)
}
// Display error dialog.
//
// Returns true on OK, false on dismiss, or ErrExtraButton.
//
// Valid options: Title, Icon, OKLabel, ExtraButton, NoWrap, Ellipsize.
func Error(text string, options ...Option) (bool, error) {
return message("--error", text, options)
}
func message(arg, text string, options []Option) (bool, error) {
opts := optsParse(options)

View file

@ -10,7 +10,7 @@ var (
messageBox = user32.NewProc("MessageBoxW")
)
func Error(text string, options ...Option) (bool, error) {
func Question(text string, options ...Option) (bool, error) {
return message(0, text, options)
}
@ -18,11 +18,11 @@ func Info(text string, options ...Option) (bool, error) {
return message(1, text, options)
}
func Question(text string, options ...Option) (bool, error) {
func Warning(text string, options ...Option) (bool, error) {
return message(2, text, options)
}
func Warning(text string, options ...Option) (bool, error) {
func Error(text string, options ...Option) (bool, error) {
return message(3, text, options)
}
@ -32,9 +32,9 @@ func message(typ int, text string, options []Option) (bool, error) {
var flags, caption uintptr
switch {
case typ == 2 && opts.extra != "":
case typ == 0 && opts.extra != "":
flags |= 0x3 // MB_YESNOCANCEL
case typ == 2 || opts.extra != "":
case typ == 0 || opts.extra != "":
flags |= 0x1 // MB_OKCANCEL
}
@ -49,7 +49,7 @@ func message(typ int, text string, options []Option) (bool, error) {
flags |= 0x40 // MB_ICONINFORMATION
}
if typ == 2 && opts.defcancel {
if typ == 0 && opts.defcancel {
if opts.extra == "" {
flags |= 0x100 // MB_DEFBUTTON2
} else {
@ -79,7 +79,7 @@ func message(typ int, text string, options []Option) (bool, error) {
if n == 0 {
return false, err
}
if n == 7 || n == 2 && typ != 2 { // IDNO
if n == 7 || n == 2 && typ != 0 { // IDNO
return false, ErrExtraButton
}
if n == 1 || n == 6 { // IDOK, IDYES
@ -107,7 +107,7 @@ func hookMessageLabels(typ int, opts options) (hook uintptr, err error) {
case 1, 6: // IDOK, IDYES
text = opts.ok
case 2: // IDCANCEL
if typ == 2 {
if typ == 0 {
text = opts.cancel
} else if opts.extra != "" {
text = opts.extra