Folder selection improvements, refactoring (windows).
This commit is contained in:
parent
58dae10a83
commit
f68fa5b0ba
5 changed files with 89 additions and 74 deletions
|
@ -1,7 +1,6 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -13,7 +12,6 @@ import (
|
||||||
var (
|
var (
|
||||||
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
|
getOpenFileName = comdlg32.NewProc("GetOpenFileNameW")
|
||||||
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
|
getSaveFileName = comdlg32.NewProc("GetSaveFileNameW")
|
||||||
commDlgExtendedError = comdlg32.NewProc("CommDlgExtendedError")
|
|
||||||
shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW")
|
shBrowseForFolder = shell32.NewProc("SHBrowseForFolderW")
|
||||||
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
|
shGetPathFromIDListEx = shell32.NewProc("SHGetPathFromIDListEx")
|
||||||
shCreateItemFromParsingName = shell32.NewProc("SHCreateItemFromParsingName")
|
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
|
_CLSID_FileOpenDialog, 0, 0x17, // CLSCTX_ALL
|
||||||
_IID_IFileOpenDialog, uintptr(unsafe.Pointer(&dialog)))
|
_IID_IFileOpenDialog, uintptr(unsafe.Pointer(&dialog)))
|
||||||
if int32(hr) < 0 {
|
if int32(hr) < 0 {
|
||||||
return browseForFolder(opts.title)
|
return browseForFolder(opts)
|
||||||
}
|
}
|
||||||
defer dialog.Call(dialog.vtbl.Release)
|
defer dialog.Call(dialog.vtbl.Release)
|
||||||
|
|
||||||
|
@ -267,12 +265,22 @@ func pickFolders(opts options, multi bool) (str string, lst []string, err error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func browseForFolder(title string) (string, []string, error) {
|
func browseForFolder(opts options) (string, []string, error) {
|
||||||
var args _BROWSEINFO
|
var args _BROWSEINFO
|
||||||
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
|
args.Flags = 0x1 // BIF_RETURNONLYFSDIRS
|
||||||
|
|
||||||
if title != "" {
|
if opts.title != "" {
|
||||||
args.Title = syscall.StringToUTF16Ptr(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)))
|
ptr, _, _ := shBrowseForFolder.Call(uintptr(unsafe.Pointer(&args)))
|
||||||
|
@ -322,15 +330,6 @@ func windowsFilters(filters []FileFilter) []uint16 {
|
||||||
return res
|
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 {
|
type _OPENFILENAME struct {
|
||||||
StructSize uint32
|
StructSize uint32
|
||||||
Owner uintptr
|
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")
|
_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 {
|
type _IFileOpenDialog struct {
|
||||||
_COMObject
|
_COMObject
|
||||||
vtbl *_IFileOpenDialogVtbl
|
vtbl *_IFileOpenDialogVtbl
|
||||||
|
@ -467,9 +449,3 @@ type _IShellItemArrayVtbl struct {
|
||||||
GetItemAt uintptr
|
GetItemAt uintptr
|
||||||
EnumItems uintptr
|
EnumItems uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
type _IUnknownVtbl struct {
|
|
||||||
QueryInterface uintptr
|
|
||||||
AddRef uintptr
|
|
||||||
Release uintptr
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import "syscall"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
comdlg32 = syscall.NewLazyDLL("comdlg32.dll")
|
comdlg32 = syscall.NewLazyDLL("comdlg32.dll")
|
||||||
|
@ -9,6 +13,8 @@ var (
|
||||||
shell32 = syscall.NewLazyDLL("shell32.dll")
|
shell32 = syscall.NewLazyDLL("shell32.dll")
|
||||||
user32 = syscall.NewLazyDLL("user32.dll")
|
user32 = syscall.NewLazyDLL("user32.dll")
|
||||||
|
|
||||||
|
commDlgExtendedError = comdlg32.NewProc("CommDlgExtendedError")
|
||||||
|
|
||||||
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
getCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
||||||
|
|
||||||
coInitializeEx = ole32.NewProc("CoInitializeEx")
|
coInitializeEx = ole32.NewProc("CoInitializeEx")
|
||||||
|
@ -16,6 +22,7 @@ var (
|
||||||
coCreateInstance = ole32.NewProc("CoCreateInstance")
|
coCreateInstance = ole32.NewProc("CoCreateInstance")
|
||||||
coTaskMemFree = ole32.NewProc("CoTaskMemFree")
|
coTaskMemFree = ole32.NewProc("CoTaskMemFree")
|
||||||
|
|
||||||
|
sendMessage = user32.NewProc("SendMessageW")
|
||||||
getClassName = user32.NewProc("GetClassNameA")
|
getClassName = user32.NewProc("GetClassNameA")
|
||||||
setWindowsHookEx = user32.NewProc("SetWindowsHookExW")
|
setWindowsHookEx = user32.NewProc("SetWindowsHookExW")
|
||||||
unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx")
|
unhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx")
|
||||||
|
@ -25,6 +32,15 @@ var (
|
||||||
setWindowText = user32.NewProc("SetWindowTextW")
|
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 {
|
type _CWPRETSTRUCT struct {
|
||||||
Result uintptr
|
Result uintptr
|
||||||
LParam uintptr
|
LParam uintptr
|
||||||
|
@ -32,3 +48,26 @@ type _CWPRETSTRUCT struct {
|
||||||
Message uint32
|
Message uint32
|
||||||
HWnd uintptr
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/ncruces/zenity/internal/osa"
|
"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)
|
return message(0, text, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,18 +14,18 @@ func Info(text string, options ...Option) (bool, error) {
|
||||||
return message(1, text, options)
|
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)
|
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)
|
return message(3, text, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func message(typ int, text string, options []Option) (bool, error) {
|
func message(typ int, text string, options []Option) (bool, error) {
|
||||||
opts := optsParse(options)
|
opts := optsParse(options)
|
||||||
data := osa.Msg{Text: text}
|
data := osa.Msg{Text: text}
|
||||||
dialog := typ == 2 || opts.icon != 0
|
dialog := typ == 0 || opts.icon != 0
|
||||||
|
|
||||||
if dialog {
|
if dialog {
|
||||||
data.Operation = "displayDialog"
|
data.Operation = "displayDialog"
|
||||||
|
@ -47,16 +47,16 @@ func message(typ int, text string, options []Option) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch typ {
|
switch typ {
|
||||||
case 0:
|
|
||||||
data.As = "critical"
|
|
||||||
case 1:
|
case 1:
|
||||||
data.As = "informational"
|
data.As = "informational"
|
||||||
case 3:
|
case 2:
|
||||||
data.As = "warning"
|
data.As = "warning"
|
||||||
|
case 3:
|
||||||
|
data.As = "critical"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ != 2 {
|
if typ != 0 {
|
||||||
if dialog {
|
if dialog {
|
||||||
opts.ok = "OK"
|
opts.ok = "OK"
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func message(typ int, text string, options []Option) (bool, error) {
|
||||||
if opts.cancel == "" {
|
if opts.cancel == "" {
|
||||||
opts.cancel = "Cancel"
|
opts.cancel = "Cancel"
|
||||||
}
|
}
|
||||||
if typ == 2 {
|
if typ == 0 {
|
||||||
if opts.extra == "" {
|
if opts.extra == "" {
|
||||||
data.Buttons = []string{opts.cancel, opts.ok}
|
data.Buttons = []string{opts.cancel, opts.ok}
|
||||||
data.Default = 2
|
data.Default = 2
|
||||||
|
|
36
msg_unix.go
36
msg_unix.go
|
@ -8,24 +8,6 @@ import (
|
||||||
"github.com/ncruces/zenity/internal/zen"
|
"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.
|
// Display question dialog.
|
||||||
//
|
//
|
||||||
// Returns true on OK, false on Cancel, or ErrExtraButton.
|
// 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)
|
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.
|
// Display warning dialog.
|
||||||
//
|
//
|
||||||
// Returns true on OK, false on dismiss, or ErrExtraButton.
|
// 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)
|
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) {
|
func message(arg, text string, options []Option) (bool, error) {
|
||||||
opts := optsParse(options)
|
opts := optsParse(options)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ var (
|
||||||
messageBox = user32.NewProc("MessageBoxW")
|
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)
|
return message(0, text, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@ func Info(text string, options ...Option) (bool, error) {
|
||||||
return message(1, text, options)
|
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)
|
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)
|
return message(3, text, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ func message(typ int, text string, options []Option) (bool, error) {
|
||||||
var flags, caption uintptr
|
var flags, caption uintptr
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case typ == 2 && opts.extra != "":
|
case typ == 0 && opts.extra != "":
|
||||||
flags |= 0x3 // MB_YESNOCANCEL
|
flags |= 0x3 // MB_YESNOCANCEL
|
||||||
case typ == 2 || opts.extra != "":
|
case typ == 0 || opts.extra != "":
|
||||||
flags |= 0x1 // MB_OKCANCEL
|
flags |= 0x1 // MB_OKCANCEL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ func message(typ int, text string, options []Option) (bool, error) {
|
||||||
flags |= 0x40 // MB_ICONINFORMATION
|
flags |= 0x40 // MB_ICONINFORMATION
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == 2 && opts.defcancel {
|
if typ == 0 && opts.defcancel {
|
||||||
if opts.extra == "" {
|
if opts.extra == "" {
|
||||||
flags |= 0x100 // MB_DEFBUTTON2
|
flags |= 0x100 // MB_DEFBUTTON2
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,7 +79,7 @@ func message(typ int, text string, options []Option) (bool, error) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if n == 7 || n == 2 && typ != 2 { // IDNO
|
if n == 7 || n == 2 && typ != 0 { // IDNO
|
||||||
return false, ErrExtraButton
|
return false, ErrExtraButton
|
||||||
}
|
}
|
||||||
if n == 1 || n == 6 { // IDOK, IDYES
|
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
|
case 1, 6: // IDOK, IDYES
|
||||||
text = opts.ok
|
text = opts.ok
|
||||||
case 2: // IDCANCEL
|
case 2: // IDCANCEL
|
||||||
if typ == 2 {
|
if typ == 0 {
|
||||||
text = opts.cancel
|
text = opts.cancel
|
||||||
} else if opts.extra != "" {
|
} else if opts.extra != "" {
|
||||||
text = opts.extra
|
text = opts.extra
|
||||||
|
|
Loading…
Reference in a new issue