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  }