github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/internal/go-common-file-dialog/cfd/iFileOpenDialog.go (about) 1 //go:build windows 2 // +build windows 3 4 package cfd 5 6 import ( 7 "github.com/AlpineAIO/wails/v2/internal/go-common-file-dialog/util" 8 "github.com/go-ole/go-ole" 9 "syscall" 10 "unsafe" 11 ) 12 13 var ( 14 fileOpenDialogCLSID = ole.NewGUID("{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}") 15 fileOpenDialogIID = ole.NewGUID("{d57c7288-d4ad-4768-be02-9d969532d960}") 16 ) 17 18 type iFileOpenDialog struct { 19 vtbl *iFileOpenDialogVtbl 20 parentWindowHandle uintptr 21 } 22 23 type iFileOpenDialogVtbl struct { 24 iFileDialogVtbl 25 26 GetResults uintptr // func (ppenum **IShellItemArray) HRESULT 27 GetSelectedItems uintptr 28 } 29 30 func newIFileOpenDialog() (*iFileOpenDialog, error) { 31 if unknown, err := ole.CreateInstance(fileOpenDialogCLSID, fileOpenDialogIID); err == nil { 32 return (*iFileOpenDialog)(unsafe.Pointer(unknown)), nil 33 } else { 34 return nil, err 35 } 36 } 37 38 func (fileOpenDialog *iFileOpenDialog) Show() error { 39 return fileOpenDialog.vtbl.show(unsafe.Pointer(fileOpenDialog), fileOpenDialog.parentWindowHandle) 40 } 41 42 func (fileOpenDialog *iFileOpenDialog) SetParentWindowHandle(hwnd uintptr) { 43 fileOpenDialog.parentWindowHandle = hwnd 44 } 45 46 func (fileOpenDialog *iFileOpenDialog) ShowAndGetResult() (string, error) { 47 isMultiselect, err := fileOpenDialog.isMultiselect() 48 if err != nil { 49 return "", err 50 } 51 if isMultiselect { 52 // We should panic as this error is caused by the developer using the library 53 panic("use ShowAndGetResults for open multiple files dialog") 54 } 55 if err := fileOpenDialog.Show(); err != nil { 56 return "", err 57 } 58 return fileOpenDialog.GetResult() 59 } 60 61 func (fileOpenDialog *iFileOpenDialog) ShowAndGetResults() ([]string, error) { 62 isMultiselect, err := fileOpenDialog.isMultiselect() 63 if err != nil { 64 return nil, err 65 } 66 if !isMultiselect { 67 // We should panic as this error is caused by the developer using the library 68 panic("use ShowAndGetResult for open single file dialog") 69 } 70 if err := fileOpenDialog.Show(); err != nil { 71 return nil, err 72 } 73 return fileOpenDialog.GetResults() 74 } 75 76 func (fileOpenDialog *iFileOpenDialog) SetTitle(title string) error { 77 return fileOpenDialog.vtbl.setTitle(unsafe.Pointer(fileOpenDialog), title) 78 } 79 80 func (fileOpenDialog *iFileOpenDialog) GetResult() (string, error) { 81 isMultiselect, err := fileOpenDialog.isMultiselect() 82 if err != nil { 83 return "", err 84 } 85 if isMultiselect { 86 // We should panic as this error is caused by the developer using the library 87 panic("use GetResults for open multiple files dialog") 88 } 89 return fileOpenDialog.vtbl.getResultString(unsafe.Pointer(fileOpenDialog)) 90 } 91 92 func (fileOpenDialog *iFileOpenDialog) Release() error { 93 return fileOpenDialog.vtbl.release(unsafe.Pointer(fileOpenDialog)) 94 } 95 96 func (fileOpenDialog *iFileOpenDialog) SetDefaultFolder(defaultFolderPath string) error { 97 return fileOpenDialog.vtbl.setDefaultFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath) 98 } 99 100 func (fileOpenDialog *iFileOpenDialog) SetFolder(defaultFolderPath string) error { 101 return fileOpenDialog.vtbl.setFolder(unsafe.Pointer(fileOpenDialog), defaultFolderPath) 102 } 103 104 func (fileOpenDialog *iFileOpenDialog) SetFileFilters(filter []FileFilter) error { 105 return fileOpenDialog.vtbl.setFileTypes(unsafe.Pointer(fileOpenDialog), filter) 106 } 107 108 func (fileOpenDialog *iFileOpenDialog) SetRole(role string) error { 109 return fileOpenDialog.vtbl.setClientGuid(unsafe.Pointer(fileOpenDialog), util.StringToUUID(role)) 110 } 111 112 // This should only be callable when the user asks for a multi select because 113 // otherwise they will be given the Dialog interface which does not expose this function. 114 func (fileOpenDialog *iFileOpenDialog) GetResults() ([]string, error) { 115 isMultiselect, err := fileOpenDialog.isMultiselect() 116 if err != nil { 117 return nil, err 118 } 119 if !isMultiselect { 120 // We should panic as this error is caused by the developer using the library 121 panic("use GetResult for open single file dialog") 122 } 123 return fileOpenDialog.vtbl.getResultsStrings(unsafe.Pointer(fileOpenDialog)) 124 } 125 126 func (fileOpenDialog *iFileOpenDialog) SetDefaultExtension(defaultExtension string) error { 127 return fileOpenDialog.vtbl.setDefaultExtension(unsafe.Pointer(fileOpenDialog), defaultExtension) 128 } 129 130 func (fileOpenDialog *iFileOpenDialog) SetFileName(initialFileName string) error { 131 return fileOpenDialog.vtbl.setFileName(unsafe.Pointer(fileOpenDialog), initialFileName) 132 } 133 134 func (fileOpenDialog *iFileOpenDialog) SetSelectedFileFilterIndex(index uint) error { 135 return fileOpenDialog.vtbl.setSelectedFileFilterIndex(unsafe.Pointer(fileOpenDialog), index) 136 } 137 138 func (fileOpenDialog *iFileOpenDialog) setPickFolders(pickFolders bool) error { 139 const FosPickfolders = 0x20 140 if pickFolders { 141 return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosPickfolders) 142 } else { 143 return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosPickfolders) 144 } 145 } 146 147 const FosAllowMultiselect = 0x200 148 149 func (fileOpenDialog *iFileOpenDialog) isMultiselect() (bool, error) { 150 options, err := fileOpenDialog.vtbl.getOptions(unsafe.Pointer(fileOpenDialog)) 151 if err != nil { 152 return false, err 153 } 154 return options&FosAllowMultiselect != 0, nil 155 } 156 157 func (fileOpenDialog *iFileOpenDialog) setIsMultiselect(isMultiselect bool) error { 158 if isMultiselect { 159 return fileOpenDialog.vtbl.addOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect) 160 } else { 161 return fileOpenDialog.vtbl.removeOption(unsafe.Pointer(fileOpenDialog), FosAllowMultiselect) 162 } 163 } 164 165 func (vtbl *iFileOpenDialogVtbl) getResults(objPtr unsafe.Pointer) (*iShellItemArray, error) { 166 var shellItemArray *iShellItemArray 167 ret, _, _ := syscall.Syscall(vtbl.GetResults, 168 1, 169 uintptr(objPtr), 170 uintptr(unsafe.Pointer(&shellItemArray)), 171 0) 172 return shellItemArray, hresultToError(ret) 173 } 174 175 func (vtbl *iFileOpenDialogVtbl) getResultsStrings(objPtr unsafe.Pointer) ([]string, error) { 176 shellItemArray, err := vtbl.getResults(objPtr) 177 if err != nil { 178 return nil, err 179 } 180 if shellItemArray == nil { 181 return nil, ErrCancelled 182 } 183 defer shellItemArray.vtbl.release(unsafe.Pointer(shellItemArray)) 184 count, err := shellItemArray.vtbl.getCount(unsafe.Pointer(shellItemArray)) 185 if err != nil { 186 return nil, err 187 } 188 var results []string 189 for i := uintptr(0); i < count; i++ { 190 newItem, err := shellItemArray.vtbl.getItemAt(unsafe.Pointer(shellItemArray), i) 191 if err != nil { 192 return nil, err 193 } 194 results = append(results, newItem) 195 } 196 return results, nil 197 }