From f68fa5b0bacb6ef06b3e3344773843bbf0753519 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Sun, 12 Jan 2020 18:55:10 +0000 Subject: [PATCH] Folder selection improvements, refactoring (windows). --- file_windows.go | 52 +++++++++++++------------------------------------ init_windows.go | 41 +++++++++++++++++++++++++++++++++++++- msg_darwin.go | 18 ++++++++--------- msg_unix.go | 36 +++++++++++++++++----------------- msg_windows.go | 16 +++++++-------- 5 files changed, 89 insertions(+), 74 deletions(-) diff --git a/file_windows.go b/file_windows.go index 5f01a81..b5c5596 100644 --- a/file_windows.go +++ b/file_windows.go @@ -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 -} diff --git a/init_windows.go b/init_windows.go index 7089f48..433c729 100644 --- a/init_windows.go +++ b/init_windows.go @@ -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 +} diff --git a/msg_darwin.go b/msg_darwin.go index dd6338b..837ef51 100644 --- a/msg_darwin.go +++ b/msg_darwin.go @@ -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 diff --git a/msg_unix.go b/msg_unix.go index 718ab8f..e125efd 100644 --- a/msg_unix.go +++ b/msg_unix.go @@ -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) diff --git a/msg_windows.go b/msg_windows.go index 87da2f4..d9bf9d4 100644 --- a/msg_windows.go +++ b/msg_windows.go @@ -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