Merge pull request #91 from ncruces/winfile
Use IFileDialog where available.
This commit is contained in:
commit
8707cba910
12 changed files with 337 additions and 102 deletions
|
@ -102,7 +102,7 @@ func (dlg *calendarDialog) setup(text string, opts options) (time.Time, error) {
|
||||||
win.SetFocus(dlg.dateCtl)
|
win.SetFocus(dlg.dateCtl)
|
||||||
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
||||||
|
|
||||||
if opts.ctx != nil {
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
wait := make(chan struct{})
|
wait := make(chan struct{})
|
||||||
defer close(wait)
|
defer close(wait)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -97,7 +97,7 @@ func (dlg *entryDialog) setup(text string, opts options) (string, error) {
|
||||||
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
||||||
win.SendMessage(dlg.editCtl, win.EM_SETSEL, 0, intptr(-1))
|
win.SendMessage(dlg.editCtl, win.EM_SETSEL, 0, intptr(-1))
|
||||||
|
|
||||||
if opts.ctx != nil {
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
wait := make(chan struct{})
|
wait := make(chan struct{})
|
||||||
defer close(wait)
|
defer close(wait)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
17
file_test.go
17
file_test.go
|
@ -3,7 +3,6 @@ package zenity_test
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -124,7 +123,7 @@ func TestSelectFile_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Cancel", func(t *testing.T) {
|
t.Run("Cancel", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file selection dialog, cancel."))
|
zenity.Info("In the file selection dialog, cancel.")
|
||||||
str, err := zenity.SelectFile()
|
str, err := zenity.SelectFile()
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -134,7 +133,7 @@ func TestSelectFile_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("File", func(t *testing.T) {
|
t.Run("File", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file selection dialog, pick any file."))
|
zenity.Info("In the file selection dialog, pick any file.")
|
||||||
str, err := zenity.SelectFile()
|
str, err := zenity.SelectFile()
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -147,7 +146,7 @@ func TestSelectFile_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("Directory", func(t *testing.T) {
|
t.Run("Directory", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file selection dialog, pick any directory."))
|
zenity.Info("In the file selection dialog, pick any directory.")
|
||||||
str, err := zenity.SelectFile(zenity.Directory())
|
str, err := zenity.SelectFile(zenity.Directory())
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -169,7 +168,7 @@ func TestSelectFileMultiple_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Cancel", func(t *testing.T) {
|
t.Run("Cancel", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file selection dialog, cancel."))
|
zenity.Info("In the file selection dialog, cancel.")
|
||||||
lst, err := zenity.SelectFileMultiple()
|
lst, err := zenity.SelectFileMultiple()
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -179,7 +178,7 @@ func TestSelectFileMultiple_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("Files", func(t *testing.T) {
|
t.Run("Files", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file selection dialog, pick two files."))
|
zenity.Info("In the file selection dialog, pick two files.")
|
||||||
lst, err := zenity.SelectFileMultiple()
|
lst, err := zenity.SelectFileMultiple()
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -194,7 +193,7 @@ func TestSelectFileMultiple_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("Directories", func(t *testing.T) {
|
t.Run("Directories", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file selection dialog, pick two directories."))
|
zenity.Info("In the file selection dialog, pick two directories.")
|
||||||
lst, err := zenity.SelectFileMultiple(zenity.Directory())
|
lst, err := zenity.SelectFileMultiple(zenity.Directory())
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -221,7 +220,7 @@ func TestSelectFileSave_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Cancel", func(t *testing.T) {
|
t.Run("Cancel", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file save dialog, cancel."))
|
zenity.Info("In the file save dialog, cancel.")
|
||||||
str, err := zenity.SelectFileSave()
|
str, err := zenity.SelectFileSave()
|
||||||
if skip, err := skip(err); skip {
|
if skip, err := skip(err); skip {
|
||||||
t.Skip("skipping:", err)
|
t.Skip("skipping:", err)
|
||||||
|
@ -231,7 +230,7 @@ func TestSelectFileSave_script(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.Run("Name", func(t *testing.T) {
|
t.Run("Name", func(t *testing.T) {
|
||||||
zenity.Info(fmt.Sprintf("In the file save dialog, press OK."))
|
zenity.Info("In the file save dialog, press OK.")
|
||||||
str, err := zenity.SelectFileSave(
|
str, err := zenity.SelectFileSave(
|
||||||
zenity.ConfirmOverwrite(),
|
zenity.ConfirmOverwrite(),
|
||||||
zenity.Filename("Χρτο.go"),
|
zenity.Filename("Χρτο.go"),
|
||||||
|
|
255
file_windows.go
255
file_windows.go
|
@ -1,8 +1,10 @@
|
||||||
package zenity
|
package zenity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
@ -11,9 +13,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func selectFile(opts options) (string, error) {
|
func selectFile(opts options) (string, error) {
|
||||||
|
name, _, shown, err := fileOpenDialog(opts, false)
|
||||||
|
if shown || opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return name, err
|
||||||
|
}
|
||||||
if opts.directory {
|
if opts.directory {
|
||||||
res, _, err := pickFolders(opts, false)
|
return browseForFolder(opts)
|
||||||
return res, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var args win.OPENFILENAME
|
var args win.OPENFILENAME
|
||||||
|
@ -54,9 +59,12 @@ func selectFile(opts options) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectFileMultiple(opts options) ([]string, error) {
|
func selectFileMultiple(opts options) ([]string, error) {
|
||||||
|
_, list, shown, err := fileOpenDialog(opts, true)
|
||||||
|
if shown || opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
if opts.directory {
|
if opts.directory {
|
||||||
_, res, err := pickFolders(opts, true)
|
return nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
|
||||||
return res, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var args win.OPENFILENAME
|
var args win.OPENFILENAME
|
||||||
|
@ -123,8 +131,11 @@ func selectFileMultiple(opts options) ([]string, error) {
|
||||||
|
|
||||||
func selectFileSave(opts options) (string, error) {
|
func selectFileSave(opts options) (string, error) {
|
||||||
if opts.directory {
|
if opts.directory {
|
||||||
res, _, err := pickFolders(opts, false)
|
return selectFile(opts)
|
||||||
return res, err
|
}
|
||||||
|
name, shown, err := fileSaveDialog(opts)
|
||||||
|
if shown || opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return name, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var args win.OPENFILENAME
|
var args win.OPENFILENAME
|
||||||
|
@ -170,53 +181,59 @@ func selectFileSave(opts options) (string, error) {
|
||||||
return syscall.UTF16ToString(res[:]), nil
|
return syscall.UTF16ToString(res[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickFolders(opts options, multi bool) (string, []string, error) {
|
func fileOpenDialog(opts options, multi bool) (string, []string, bool, error) {
|
||||||
|
uninit, err := coInitialize()
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, false, err
|
||||||
|
}
|
||||||
|
defer uninit()
|
||||||
|
|
||||||
owner, _ := opts.attach.(win.HWND)
|
owner, _ := opts.attach.(win.HWND)
|
||||||
defer setup(owner)()
|
defer setup(owner)()
|
||||||
|
|
||||||
err := win.CoInitializeEx(0, win.COINIT_APARTMENTTHREADED|win.COINIT_DISABLE_OLE1DDE)
|
|
||||||
if err != win.RPC_E_CHANGED_MODE {
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
defer win.CoUninitialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
var dialog *win.IFileOpenDialog
|
var dialog *win.IFileOpenDialog
|
||||||
err = win.CoCreateInstance(
|
err = win.CoCreateInstance(
|
||||||
win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL,
|
win.CLSID_FileOpenDialog, nil, win.CLSCTX_ALL,
|
||||||
win.IID_IFileOpenDialog, unsafe.Pointer(&dialog))
|
win.IID_IFileOpenDialog, unsafe.Pointer(&dialog))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if multi {
|
return "", nil, false, err
|
||||||
return "", nil, fmt.Errorf("%w: multiple directory", ErrUnsupported)
|
|
||||||
}
|
|
||||||
return browseForFolder(opts)
|
|
||||||
}
|
}
|
||||||
defer dialog.Release()
|
defer dialog.Release()
|
||||||
|
|
||||||
flgs, err := dialog.GetOptions()
|
flgs, err := dialog.GetOptions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, false, err
|
||||||
}
|
}
|
||||||
flgs |= win.FOS_NOCHANGEDIR | win.FOS_PICKFOLDERS | win.FOS_FORCEFILESYSTEM
|
flgs |= win.FOS_NOCHANGEDIR | win.FOS_FILEMUSTEXIST | win.FOS_FORCEFILESYSTEM
|
||||||
if multi {
|
if multi {
|
||||||
flgs |= win.FOS_ALLOWMULTISELECT
|
flgs |= win.FOS_ALLOWMULTISELECT
|
||||||
}
|
}
|
||||||
|
if opts.directory {
|
||||||
|
flgs |= win.FOS_PICKFOLDERS
|
||||||
|
}
|
||||||
if opts.showHidden {
|
if opts.showHidden {
|
||||||
flgs |= win.FOS_FORCESHOWHIDDEN
|
flgs |= win.FOS_FORCESHOWHIDDEN
|
||||||
}
|
}
|
||||||
err = dialog.SetOptions(flgs)
|
err = dialog.SetOptions(flgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.title != nil {
|
if opts.title != nil {
|
||||||
dialog.SetTitle(strptr(*opts.title))
|
dialog.SetTitle(strptr(*opts.title))
|
||||||
}
|
}
|
||||||
|
if opts.fileFilters != nil {
|
||||||
|
dialog.SetFileTypes(initFileTypes(opts.fileFilters))
|
||||||
|
}
|
||||||
|
|
||||||
if opts.filename != "" {
|
if opts.filename != "" {
|
||||||
var item *win.IShellItem
|
var item *win.IShellItem
|
||||||
win.SHCreateItemFromParsingName(strptr(opts.filename), nil, win.IID_IShellItem, &item)
|
dir, name, _ := splitDirAndName(opts.filename)
|
||||||
|
dialog.SetFileName(strptr(name))
|
||||||
|
if ext := filepath.Ext(name); len(ext) > 1 {
|
||||||
|
dialog.SetDefaultExtension(strptr(ext[1:]))
|
||||||
|
}
|
||||||
|
win.SHCreateItemFromParsingName(strptr(dir), nil, win.IID_IShellItem, &item)
|
||||||
if item != nil {
|
if item != nil {
|
||||||
defer item.Release()
|
defer item.Release()
|
||||||
dialog.SetFolder(item)
|
dialog.SetFolder(item)
|
||||||
|
@ -225,51 +242,158 @@ func pickFolders(opts options, multi bool) (string, []string, error) {
|
||||||
|
|
||||||
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, false, err
|
||||||
}
|
}
|
||||||
defer unhook()
|
defer unhook()
|
||||||
|
|
||||||
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
|
wait := make(chan struct{})
|
||||||
|
defer close(wait)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-opts.ctx.Done():
|
||||||
|
dialog.Close(win.E_TIMEOUT)
|
||||||
|
case <-wait:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
err = dialog.Show(owner)
|
err = dialog.Show(owner)
|
||||||
if opts.ctx != nil && opts.ctx.Err() != nil {
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
return "", nil, opts.ctx.Err()
|
return "", nil, true, opts.ctx.Err()
|
||||||
}
|
}
|
||||||
if err == win.E_CANCELED {
|
if err == win.E_CANCELED {
|
||||||
return "", nil, ErrCanceled
|
return "", nil, true, ErrCanceled
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if multi {
|
if multi {
|
||||||
items, err := dialog.GetResults()
|
items, err := dialog.GetResults()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, true, err
|
||||||
}
|
}
|
||||||
defer items.Release()
|
defer items.Release()
|
||||||
|
|
||||||
count, err := items.GetCount()
|
count, err := items.GetCount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var lst []string
|
var lst []string
|
||||||
for i := uint32(0); i < count && err == nil; i++ {
|
for i := uint32(0); i < count && err == nil; i++ {
|
||||||
str, err := shellItemPath(items.GetItemAt(i))
|
str, err := shellItemPath(items.GetItemAt(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, true, err
|
||||||
}
|
}
|
||||||
lst = append(lst, str)
|
lst = append(lst, str)
|
||||||
}
|
}
|
||||||
return "", lst, nil
|
return "", lst, true, nil
|
||||||
} else {
|
} else {
|
||||||
str, err := shellItemPath(dialog.GetResult())
|
str, err := shellItemPath(dialog.GetResult())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, true, err
|
||||||
}
|
}
|
||||||
return str, nil, nil
|
return str, nil, true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fileSaveDialog(opts options) (string, bool, error) {
|
||||||
|
uninit, err := coInitialize()
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
defer uninit()
|
||||||
|
|
||||||
|
owner, _ := opts.attach.(win.HWND)
|
||||||
|
defer setup(owner)()
|
||||||
|
|
||||||
|
var dialog *win.IFileSaveDialog
|
||||||
|
err = win.CoCreateInstance(
|
||||||
|
win.CLSID_FileSaveDialog, nil, win.CLSCTX_ALL,
|
||||||
|
win.IID_IFileSaveDialog, unsafe.Pointer(&dialog))
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
defer dialog.Release()
|
||||||
|
|
||||||
|
flgs, err := dialog.GetOptions()
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
flgs |= win.FOS_NOCHANGEDIR | win.FOS_PATHMUSTEXIST | win.FOS_NOREADONLYRETURN | win.FOS_FORCEFILESYSTEM
|
||||||
|
if opts.confirmOverwrite {
|
||||||
|
flgs |= win.FOS_OVERWRITEPROMPT
|
||||||
|
}
|
||||||
|
if opts.confirmCreate {
|
||||||
|
flgs |= win.FOS_CREATEPROMPT
|
||||||
|
}
|
||||||
|
if opts.showHidden {
|
||||||
|
flgs |= win.FOS_FORCESHOWHIDDEN
|
||||||
|
}
|
||||||
|
err = dialog.SetOptions(flgs)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.title != nil {
|
||||||
|
dialog.SetTitle(strptr(*opts.title))
|
||||||
|
}
|
||||||
|
if opts.fileFilters != nil {
|
||||||
|
dialog.SetFileTypes(initFileTypes(opts.fileFilters))
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.filename != "" {
|
||||||
|
var item *win.IShellItem
|
||||||
|
dir, name, _ := splitDirAndName(opts.filename)
|
||||||
|
dialog.SetFileName(strptr(name))
|
||||||
|
if ext := filepath.Ext(name); len(ext) > 1 {
|
||||||
|
dialog.SetDefaultExtension(strptr(ext[1:]))
|
||||||
|
}
|
||||||
|
win.SHCreateItemFromParsingName(strptr(dir), nil, win.IID_IShellItem, &item)
|
||||||
|
if item != nil {
|
||||||
|
defer item.Release()
|
||||||
|
dialog.SetFolder(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
defer unhook()
|
||||||
|
|
||||||
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
|
wait := make(chan struct{})
|
||||||
|
defer close(wait)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-opts.ctx.Done():
|
||||||
|
dialog.Close(win.E_TIMEOUT)
|
||||||
|
case <-wait:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dialog.Show(owner)
|
||||||
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
|
return "", true, opts.ctx.Err()
|
||||||
|
}
|
||||||
|
if err == win.E_CANCELED {
|
||||||
|
return "", true, ErrCanceled
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
str, err := shellItemPath(dialog.GetResult())
|
||||||
|
if err != nil {
|
||||||
|
return "", true, err
|
||||||
|
}
|
||||||
|
return str, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func shellItemPath(item *win.IShellItem, err error) (string, error) {
|
func shellItemPath(item *win.IShellItem, err error) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -278,7 +402,13 @@ func shellItemPath(item *win.IShellItem, err error) (string, error) {
|
||||||
return item.GetDisplayName(win.SIGDN_FILESYSPATH)
|
return item.GetDisplayName(win.SIGDN_FILESYSPATH)
|
||||||
}
|
}
|
||||||
|
|
||||||
func browseForFolder(opts options) (string, []string, error) {
|
func browseForFolder(opts options) (string, error) {
|
||||||
|
uninit, err := coInitialize()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer uninit()
|
||||||
|
|
||||||
var args win.BROWSEINFO
|
var args win.BROWSEINFO
|
||||||
args.Owner, _ = opts.attach.(win.HWND)
|
args.Owner, _ = opts.attach.(win.HWND)
|
||||||
args.Flags = win.BIF_RETURNONLYFSDIRS
|
args.Flags = win.BIF_RETURNONLYFSDIRS
|
||||||
|
@ -293,16 +423,16 @@ func browseForFolder(opts options) (string, []string, error) {
|
||||||
|
|
||||||
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
unhook, err := hookDialog(opts.ctx, opts.windowIcon, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
defer unhook()
|
defer unhook()
|
||||||
|
|
||||||
ptr := win.SHBrowseForFolder(&args)
|
ptr := win.SHBrowseForFolder(&args)
|
||||||
if opts.ctx != nil && opts.ctx.Err() != nil {
|
if opts.ctx != nil && opts.ctx.Err() != nil {
|
||||||
return "", nil, opts.ctx.Err()
|
return "", opts.ctx.Err()
|
||||||
}
|
}
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
return "", nil, ErrCanceled
|
return "", ErrCanceled
|
||||||
}
|
}
|
||||||
defer win.CoTaskMemFree(unsafe.Pointer(ptr))
|
defer win.CoTaskMemFree(unsafe.Pointer(ptr))
|
||||||
|
|
||||||
|
@ -310,7 +440,7 @@ func browseForFolder(opts options) (string, []string, error) {
|
||||||
win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0)
|
win.SHGetPathFromIDListEx(ptr, &res[0], len(res), 0)
|
||||||
|
|
||||||
str := syscall.UTF16ToString(res[:])
|
str := syscall.UTF16ToString(res[:])
|
||||||
return str, []string{str}, nil
|
return str, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uintptr {
|
func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uintptr {
|
||||||
|
@ -320,6 +450,27 @@ func browseForFolderCallback(wnd win.HWND, msg uint32, lparam, data uintptr) uin
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func coInitialize() (context.CancelFunc, error) {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
// .NET uses MTA for all background threads, so do the same.
|
||||||
|
// If someone needs STA because they're doing UI,
|
||||||
|
// they should initialize COM themselves before.
|
||||||
|
err := win.CoInitializeEx(0, win.COINIT_MULTITHREADED|win.COINIT_DISABLE_OLE1DDE)
|
||||||
|
if err == win.S_FALSE {
|
||||||
|
// COM was already initialized, we simply increased the ref count.
|
||||||
|
// Make this a no-op by decreasing our ref count.
|
||||||
|
win.CoUninitialize()
|
||||||
|
return runtime.UnlockOSThread, nil
|
||||||
|
}
|
||||||
|
// Don't uninitialize COM; this is against the docs, but it's what .NET does.
|
||||||
|
// Eventually all threads will have COM initialized.
|
||||||
|
if err == nil || err == win.RPC_E_CHANGED_MODE {
|
||||||
|
return runtime.UnlockOSThread, nil
|
||||||
|
}
|
||||||
|
runtime.UnlockOSThread()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
func initDirNameExt(filename string, name []uint16) (dir *uint16, ext *uint16) {
|
func initDirNameExt(filename string, name []uint16) (dir *uint16, ext *uint16) {
|
||||||
d, n, _ := splitDirAndName(filename)
|
d, n, _ := splitDirAndName(filename)
|
||||||
e := filepath.Ext(n)
|
e := filepath.Ext(n)
|
||||||
|
@ -343,11 +494,10 @@ func initFilters(filters FileFilters) *uint16 {
|
||||||
if len(f.Patterns) == 0 {
|
if len(f.Patterns) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
res = append(res, utf16.Encode([]rune(f.Name))...)
|
res = append(res, syscall.StringToUTF16(f.Name)...)
|
||||||
res = append(res, 0)
|
|
||||||
for _, p := range f.Patterns {
|
for _, p := range f.Patterns {
|
||||||
res = append(res, utf16.Encode([]rune(p))...)
|
res = append(res, syscall.StringToUTF16(p)...)
|
||||||
res = append(res, uint16(';'))
|
res[len(res)-1] = ';'
|
||||||
}
|
}
|
||||||
res = append(res, 0)
|
res = append(res, 0)
|
||||||
}
|
}
|
||||||
|
@ -357,3 +507,24 @@ func initFilters(filters FileFilters) *uint16 {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initFileTypes(filters FileFilters) (int, *win.COMDLG_FILTERSPEC) {
|
||||||
|
filters.simplify()
|
||||||
|
filters.name()
|
||||||
|
var res []win.COMDLG_FILTERSPEC
|
||||||
|
for _, f := range filters {
|
||||||
|
if len(f.Patterns) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var spec []uint16
|
||||||
|
for _, p := range f.Patterns {
|
||||||
|
spec = append(spec, syscall.StringToUTF16(p)...)
|
||||||
|
spec[len(spec)-1] = ';'
|
||||||
|
}
|
||||||
|
res = append(res, win.COMDLG_FILTERSPEC{
|
||||||
|
Name: syscall.StringToUTF16Ptr(f.Name),
|
||||||
|
Spec: &spec[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return len(res), &res[0]
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,9 @@ const (
|
||||||
CLSCTX_ALL = windows.CLSCTX_INPROC_SERVER | windows.CLSCTX_INPROC_HANDLER | windows.CLSCTX_LOCAL_SERVER | windows.CLSCTX_REMOTE_SERVER
|
CLSCTX_ALL = windows.CLSCTX_INPROC_SERVER | windows.CLSCTX_INPROC_HANDLER | windows.CLSCTX_LOCAL_SERVER | windows.CLSCTX_REMOTE_SERVER
|
||||||
|
|
||||||
E_CANCELED = windows.ERROR_CANCELLED | windows.FACILITY_WIN32<<16 | 0x80000000
|
E_CANCELED = windows.ERROR_CANCELLED | windows.FACILITY_WIN32<<16 | 0x80000000
|
||||||
|
E_TIMEOUT = windows.ERROR_TIMEOUT | windows.FACILITY_WIN32<<16 | 0x80000000
|
||||||
RPC_E_CHANGED_MODE = syscall.Errno(windows.RPC_E_CHANGED_MODE)
|
RPC_E_CHANGED_MODE = syscall.Errno(windows.RPC_E_CHANGED_MODE)
|
||||||
|
S_FALSE = syscall.Errno(windows.S_FALSE)
|
||||||
)
|
)
|
||||||
|
|
||||||
func CoInitializeEx(reserved uintptr, coInit uint32) error {
|
func CoInitializeEx(reserved uintptr, coInit uint32) error {
|
||||||
|
|
|
@ -46,43 +46,51 @@ const (
|
||||||
// NOTIFYICONDATA state
|
// NOTIFYICONDATA state
|
||||||
NIS_HIDDEN = 0x1
|
NIS_HIDDEN = 0x1
|
||||||
NIS_SHAREDICON = 0x2
|
NIS_SHAREDICON = 0x2
|
||||||
|
)
|
||||||
|
|
||||||
// IFileOpenDialog options
|
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-_fileopendialogoptions
|
||||||
FOS_OVERWRITEPROMPT = 0x2
|
type _FILEOPENDIALOGOPTIONS int
|
||||||
FOS_STRICTFILETYPES = 0x4
|
|
||||||
FOS_NOCHANGEDIR = 0x8
|
|
||||||
FOS_PICKFOLDERS = 0x20
|
|
||||||
FOS_FORCEFILESYSTEM = 0x40
|
|
||||||
FOS_ALLNONSTORAGEITEMS = 0x80
|
|
||||||
FOS_NOVALIDATE = 0x100
|
|
||||||
FOS_ALLOWMULTISELECT = 0x200
|
|
||||||
FOS_PATHMUSTEXIST = 0x800
|
|
||||||
FOS_FILEMUSTEXIST = 0x1000
|
|
||||||
FOS_CREATEPROMPT = 0x2000
|
|
||||||
FOS_SHAREAWARE = 0x4000
|
|
||||||
FOS_NOREADONLYRETURN = 0x8000
|
|
||||||
FOS_NOTESTFILECREATE = 0x10000
|
|
||||||
FOS_HIDEMRUPLACES = 0x20000
|
|
||||||
FOS_HIDEPINNEDPLACES = 0x40000
|
|
||||||
FOS_NODEREFERENCELINKS = 0x100000
|
|
||||||
FOS_OKBUTTONNEEDSINTERACTION = 0x200000
|
|
||||||
FOS_DONTADDTORECENT = 0x2000000
|
|
||||||
FOS_FORCESHOWHIDDEN = 0x10000000
|
|
||||||
FOS_DEFAULTNOMINIMODE = 0x20000000
|
|
||||||
FOS_FORCEPREVIEWPANEON = 0x40000000
|
|
||||||
FOS_SUPPORTSTREAMABLEITEMS = 0x80000000
|
|
||||||
|
|
||||||
// IShellItem.GetDisplayName forms
|
const (
|
||||||
SIGDN_NORMALDISPLAY = 0x00000000
|
FOS_OVERWRITEPROMPT _FILEOPENDIALOGOPTIONS = 0x2
|
||||||
SIGDN_PARENTRELATIVEPARSING = ^(^0x18001 + 0x80000000)
|
FOS_STRICTFILETYPES _FILEOPENDIALOGOPTIONS = 0x4
|
||||||
SIGDN_DESKTOPABSOLUTEPARSING = ^(^0x28000 + 0x80000000)
|
FOS_NOCHANGEDIR _FILEOPENDIALOGOPTIONS = 0x8
|
||||||
SIGDN_PARENTRELATIVEEDITING = ^(^0x31001 + 0x80000000)
|
FOS_PICKFOLDERS _FILEOPENDIALOGOPTIONS = 0x20
|
||||||
SIGDN_DESKTOPABSOLUTEEDITING = ^(^0x4c000 + 0x80000000)
|
FOS_FORCEFILESYSTEM _FILEOPENDIALOGOPTIONS = 0x40
|
||||||
SIGDN_FILESYSPATH = ^(^0x58000 + 0x80000000)
|
FOS_ALLNONSTORAGEITEMS _FILEOPENDIALOGOPTIONS = 0x80
|
||||||
SIGDN_URL = ^(^0x68000 + 0x80000000)
|
FOS_NOVALIDATE _FILEOPENDIALOGOPTIONS = 0x100
|
||||||
SIGDN_PARENTRELATIVEFORADDRESSBAR = ^(^0x7c001 + 0x80000000)
|
FOS_ALLOWMULTISELECT _FILEOPENDIALOGOPTIONS = 0x200
|
||||||
SIGDN_PARENTRELATIVE = ^(^0x80001 + 0x80000000)
|
FOS_PATHMUSTEXIST _FILEOPENDIALOGOPTIONS = 0x800
|
||||||
SIGDN_PARENTRELATIVEFORUI = ^(^0x94001 + 0x80000000)
|
FOS_FILEMUSTEXIST _FILEOPENDIALOGOPTIONS = 0x1000
|
||||||
|
FOS_CREATEPROMPT _FILEOPENDIALOGOPTIONS = 0x2000
|
||||||
|
FOS_SHAREAWARE _FILEOPENDIALOGOPTIONS = 0x4000
|
||||||
|
FOS_NOREADONLYRETURN _FILEOPENDIALOGOPTIONS = 0x8000
|
||||||
|
FOS_NOTESTFILECREATE _FILEOPENDIALOGOPTIONS = 0x10000
|
||||||
|
FOS_HIDEMRUPLACES _FILEOPENDIALOGOPTIONS = 0x20000
|
||||||
|
FOS_HIDEPINNEDPLACES _FILEOPENDIALOGOPTIONS = 0x40000
|
||||||
|
FOS_NODEREFERENCELINKS _FILEOPENDIALOGOPTIONS = 0x100000
|
||||||
|
FOS_OKBUTTONNEEDSINTERACTION _FILEOPENDIALOGOPTIONS = 0x200000
|
||||||
|
FOS_DONTADDTORECENT _FILEOPENDIALOGOPTIONS = 0x2000000
|
||||||
|
FOS_FORCESHOWHIDDEN _FILEOPENDIALOGOPTIONS = 0x10000000
|
||||||
|
FOS_DEFAULTNOMINIMODE _FILEOPENDIALOGOPTIONS = 0x20000000
|
||||||
|
FOS_FORCEPREVIEWPANEON _FILEOPENDIALOGOPTIONS = 0x40000000
|
||||||
|
FOS_SUPPORTSTREAMABLEITEMS _FILEOPENDIALOGOPTIONS = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-sigdn
|
||||||
|
type SIGDN int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SIGDN_NORMALDISPLAY SIGDN = 0x00000000
|
||||||
|
SIGDN_PARENTRELATIVEPARSING SIGDN = ^(^0x18001 + 0x80000000)
|
||||||
|
SIGDN_DESKTOPABSOLUTEPARSING SIGDN = ^(^0x28000 + 0x80000000)
|
||||||
|
SIGDN_PARENTRELATIVEEDITING SIGDN = ^(^0x31001 + 0x80000000)
|
||||||
|
SIGDN_DESKTOPABSOLUTEEDITING SIGDN = ^(^0x4c000 + 0x80000000)
|
||||||
|
SIGDN_FILESYSPATH SIGDN = ^(^0x58000 + 0x80000000)
|
||||||
|
SIGDN_URL SIGDN = ^(^0x68000 + 0x80000000)
|
||||||
|
SIGDN_PARENTRELATIVEFORADDRESSBAR SIGDN = ^(^0x7c001 + 0x80000000)
|
||||||
|
SIGDN_PARENTRELATIVE SIGDN = ^(^0x80001 + 0x80000000)
|
||||||
|
SIGDN_PARENTRELATIVEFORUI SIGDN = ^(^0x94001 + 0x80000000)
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow
|
// https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/ns-shlobj_core-browseinfow
|
||||||
|
@ -116,14 +124,23 @@ type NOTIFYICONDATA struct {
|
||||||
// BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE
|
// BalloonIcon Handle // NOTIFYICONDATAA_V3_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
type IDLIST struct{}
|
// https://docs.microsoft.com/en-us/windows/win32/api/shtypes/ns-shtypes-comdlg_filterspec
|
||||||
|
type COMDLG_FILTERSPEC struct {
|
||||||
|
Name *uint16
|
||||||
|
Spec *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/shtypes/ns-shtypes-itemidlist
|
||||||
|
type ITEMIDLIST struct{}
|
||||||
|
|
||||||
// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
|
// https://github.com/wine-mirror/wine/blob/master/include/shobjidl.idl
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IID_IShellItem = guid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe")
|
IID_IShellItem = guid("\x1e\x6d\x82\x43\x18\xe7\xee\x42\xbc\x55\xa1\xe2\x61\xc3\x7b\xfe")
|
||||||
IID_IFileOpenDialog = guid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60")
|
IID_IFileOpenDialog = guid("\x88\x72\x7c\xd5\xad\xd4\x68\x47\xbe\x02\x9d\x96\x95\x32\xd9\x60")
|
||||||
|
IID_IFileSaveDialog = guid("\x23\xcd\xbc\x84\xde\x5f\xdb\x4c\xae\xa4\xaf\x64\xb8\x3d\x78\xab")
|
||||||
CLSID_FileOpenDialog = guid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
|
CLSID_FileOpenDialog = guid("\x9c\x5a\x1c\xdc\x8a\xe8\xde\x4d\xa5\xa1\x60\xf8\x2a\x20\xae\xf7")
|
||||||
|
CLSID_FileSaveDialog = guid("\xf3\xe2\xb4\xc0\x21\xba\x73\x47\x8d\xba\x33\x5e\xc9\x46\xeb\x8b")
|
||||||
)
|
)
|
||||||
|
|
||||||
type IFileOpenDialog struct{ IFileDialog }
|
type IFileOpenDialog struct{ IFileDialog }
|
||||||
|
@ -142,6 +159,16 @@ func (u *IFileOpenDialog) GetResults() (res *IShellItemArray, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IFileSaveDialog struct{ IFileDialog }
|
||||||
|
type IFileSaveDialogVtbl struct {
|
||||||
|
iFileDialogVtbl
|
||||||
|
SetSaveAsItem uintptr
|
||||||
|
SetProperties uintptr
|
||||||
|
SetCollectedProperties uintptr
|
||||||
|
GetProperties uintptr
|
||||||
|
ApplyProperties uintptr
|
||||||
|
}
|
||||||
|
|
||||||
type IFileDialog struct{ IModalWindow }
|
type IFileDialog struct{ IModalWindow }
|
||||||
type iFileDialogVtbl struct {
|
type iFileDialogVtbl struct {
|
||||||
iModalWindowVtbl
|
iModalWindowVtbl
|
||||||
|
@ -170,7 +197,16 @@ type iFileDialogVtbl struct {
|
||||||
SetFilter uintptr
|
SetFilter uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IFileDialog) SetOptions(fos int) (err error) {
|
func (u *IFileDialog) SetFileTypes(cFileTypes int, rgFilterSpec *COMDLG_FILTERSPEC) (err error) {
|
||||||
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
|
hr, _, _ := u.call(vtbl.SetFileTypes, uintptr(cFileTypes), uintptr(unsafe.Pointer(rgFilterSpec)))
|
||||||
|
if hr != 0 {
|
||||||
|
err = syscall.Errno(hr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *IFileDialog) SetOptions(fos _FILEOPENDIALOGOPTIONS) (err error) {
|
||||||
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
hr, _, _ := u.call(vtbl.SetOptions, uintptr(fos))
|
hr, _, _ := u.call(vtbl.SetOptions, uintptr(fos))
|
||||||
if hr != 0 {
|
if hr != 0 {
|
||||||
|
@ -179,7 +215,7 @@ func (u *IFileDialog) SetOptions(fos int) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IFileDialog) GetOptions() (fos int, err error) {
|
func (u *IFileDialog) GetOptions() (fos _FILEOPENDIALOGOPTIONS, err error) {
|
||||||
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
hr, _, _ := u.call(vtbl.GetOptions, uintptr(unsafe.Pointer(&fos)))
|
hr, _, _ := u.call(vtbl.GetOptions, uintptr(unsafe.Pointer(&fos)))
|
||||||
if hr != 0 {
|
if hr != 0 {
|
||||||
|
@ -197,6 +233,15 @@ func (u *IFileDialog) SetFolder(item *IShellItem) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *IFileDialog) SetFileName(name *uint16) (err error) {
|
||||||
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
|
hr, _, _ := u.call(vtbl.SetFileName, uintptr(unsafe.Pointer(name)))
|
||||||
|
if hr != 0 {
|
||||||
|
err = syscall.Errno(hr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (u *IFileDialog) SetTitle(title *uint16) (err error) {
|
func (u *IFileDialog) SetTitle(title *uint16) (err error) {
|
||||||
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
hr, _, _ := u.call(vtbl.SetTitle, uintptr(unsafe.Pointer(title)))
|
hr, _, _ := u.call(vtbl.SetTitle, uintptr(unsafe.Pointer(title)))
|
||||||
|
@ -215,6 +260,24 @@ func (u *IFileDialog) GetResult() (item *IShellItem, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *IFileDialog) SetDefaultExtension(extension *uint16) (err error) {
|
||||||
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
|
hr, _, _ := u.call(vtbl.SetDefaultExtension, uintptr(unsafe.Pointer(extension)))
|
||||||
|
if hr != 0 {
|
||||||
|
err = syscall.Errno(hr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *IFileDialog) Close(res syscall.Errno) (err error) {
|
||||||
|
vtbl := *(**iFileDialogVtbl)(unsafe.Pointer(u))
|
||||||
|
hr, _, _ := u.call(vtbl.Close, uintptr(res))
|
||||||
|
if hr != 0 {
|
||||||
|
err = syscall.Errno(hr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type IModalWindow struct{ IUnknown }
|
type IModalWindow struct{ IUnknown }
|
||||||
type iModalWindowVtbl struct {
|
type iModalWindowVtbl struct {
|
||||||
iUnknownVtbl
|
iUnknownVtbl
|
||||||
|
@ -240,7 +303,7 @@ type iShellItemVtbl struct {
|
||||||
Compare uintptr
|
Compare uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *IShellItem) GetDisplayName(name int) (res string, err error) {
|
func (u *IShellItem) GetDisplayName(name SIGDN) (res string, err error) {
|
||||||
var ptr *uint16
|
var ptr *uint16
|
||||||
vtbl := *(**iShellItemVtbl)(unsafe.Pointer(u))
|
vtbl := *(**iShellItemVtbl)(unsafe.Pointer(u))
|
||||||
hr, _, _ := u.call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr)))
|
hr, _, _ := u.call(vtbl.GetDisplayName, uintptr(name), uintptr(unsafe.Pointer(&ptr)))
|
||||||
|
@ -284,7 +347,7 @@ func (u *IShellItemArray) GetItemAt(index uint32) (item *IShellItem, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
//sys ExtractAssociatedIcon(instance Handle, path *uint16, icon *uint16) (ret Handle, err error) = shell32.ExtractAssociatedIconW
|
//sys ExtractAssociatedIcon(instance Handle, path *uint16, icon *uint16) (ret Handle, err error) = shell32.ExtractAssociatedIconW
|
||||||
//sys SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) = shell32.SHBrowseForFolder
|
//sys SHBrowseForFolder(bi *BROWSEINFO) (ret *ITEMIDLIST) = shell32.SHBrowseForFolder
|
||||||
//sys SHCreateItemFromParsingName(path *uint16, bc *IBindCtx, iid *GUID, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName
|
//sys SHCreateItemFromParsingName(path *uint16, bc *IBindCtx, iid *GUID, item **IShellItem) (res error) = shell32.SHCreateItemFromParsingName
|
||||||
//sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ok bool) = shell32.Shell_NotifyIconW
|
//sys ShellNotifyIcon(message uint32, data *NOTIFYICONDATA) (ok bool) = shell32.Shell_NotifyIconW
|
||||||
//sys SHGetPathFromIDListEx(ptr *IDLIST, path *uint16, pathLen int, opts int) (ok bool) = shell32.SHGetPathFromIDListEx
|
//sys SHGetPathFromIDListEx(ptr *ITEMIDLIST, path *uint16, pathLen int, opts int) (ok bool) = shell32.SHGetPathFromIDListEx
|
||||||
|
|
|
@ -235,9 +235,9 @@ func ExtractAssociatedIcon(instance Handle, path *uint16, icon *uint16) (ret Han
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func SHBrowseForFolder(bi *BROWSEINFO) (ret *IDLIST) {
|
func SHBrowseForFolder(bi *BROWSEINFO) (ret *ITEMIDLIST) {
|
||||||
r0, _, _ := syscall.Syscall(procSHBrowseForFolder.Addr(), 1, uintptr(unsafe.Pointer(bi)), 0, 0)
|
r0, _, _ := syscall.Syscall(procSHBrowseForFolder.Addr(), 1, uintptr(unsafe.Pointer(bi)), 0, 0)
|
||||||
ret = (*IDLIST)(unsafe.Pointer(r0))
|
ret = (*ITEMIDLIST)(unsafe.Pointer(r0))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ func SHCreateItemFromParsingName(path *uint16, bc *IBindCtx, iid *GUID, item **I
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func SHGetPathFromIDListEx(ptr *IDLIST, path *uint16, pathLen int, opts int) (ok bool) {
|
func SHGetPathFromIDListEx(ptr *ITEMIDLIST, path *uint16, pathLen int, opts int) (ok bool) {
|
||||||
r0, _, _ := syscall.Syscall6(procSHGetPathFromIDListEx.Addr(), 4, uintptr(unsafe.Pointer(ptr)), uintptr(unsafe.Pointer(path)), uintptr(pathLen), uintptr(opts), 0, 0)
|
r0, _, _ := syscall.Syscall6(procSHGetPathFromIDListEx.Addr(), 4, uintptr(unsafe.Pointer(ptr)), uintptr(unsafe.Pointer(path)), uintptr(pathLen), uintptr(opts), 0, 0)
|
||||||
ok = r0 != 0
|
ok = r0 != 0
|
||||||
return
|
return
|
||||||
|
|
|
@ -128,7 +128,7 @@ func (dlg *listDialog) setup(text string, opts options) ([]string, error) {
|
||||||
win.SetFocus(dlg.listCtl)
|
win.SetFocus(dlg.listCtl)
|
||||||
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
||||||
|
|
||||||
if opts.ctx != nil {
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
wait := make(chan struct{})
|
wait := make(chan struct{})
|
||||||
defer close(wait)
|
defer close(wait)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -121,7 +121,7 @@ func shellNotify(text string, opts options) error {
|
||||||
major, minor, _ := win.RtlGetNtVersionNumbers()
|
major, minor, _ := win.RtlGetNtVersionNumbers()
|
||||||
// On Windows 7 (6.1) and lower, wait up to 10 seconds to clean up.
|
// On Windows 7 (6.1) and lower, wait up to 10 seconds to clean up.
|
||||||
if major < 6 || major == 6 && minor < 2 {
|
if major < 6 || major == 6 && minor < 2 {
|
||||||
if opts.ctx != nil {
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
select {
|
select {
|
||||||
case <-opts.ctx.Done():
|
case <-opts.ctx.Done():
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
|
|
|
@ -190,7 +190,7 @@ func (dlg *progressDialog) setup(opts options) error {
|
||||||
}
|
}
|
||||||
once.Do(dlg.init.Done)
|
once.Do(dlg.init.Done)
|
||||||
|
|
||||||
if opts.ctx != nil {
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
wait := make(chan struct{})
|
wait := make(chan struct{})
|
||||||
defer close(wait)
|
defer close(wait)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -113,7 +113,7 @@ func (dlg *passwordDialog) setup(opts options) (string, string, error) {
|
||||||
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
win.ShowWindow(dlg.wnd, win.SW_NORMAL)
|
||||||
win.SendMessage(dlg.uEditCtl, win.EM_SETSEL, 0, intptr(-1))
|
win.SendMessage(dlg.uEditCtl, win.EM_SETSEL, 0, intptr(-1))
|
||||||
|
|
||||||
if opts.ctx != nil {
|
if opts.ctx != nil && opts.ctx.Done() != nil {
|
||||||
wait := make(chan struct{})
|
wait := make(chan struct{})
|
||||||
defer close(wait)
|
defer close(wait)
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -118,7 +118,7 @@ func newDialogHook(ctx context.Context, icon any, title *string, init func(wnd w
|
||||||
title: title,
|
title: title,
|
||||||
init: init,
|
init: init,
|
||||||
}
|
}
|
||||||
if ctx != nil {
|
if ctx != nil && ctx.Done() != nil {
|
||||||
hook.done = make(chan struct{})
|
hook.done = make(chan struct{})
|
||||||
go hook.wait()
|
go hook.wait()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue