@ -1,5 +1,4 @@
using System ;
using System.Linq ;
using System.Collections.Generic ;
using System.IO ;
using System.ComponentModel ;
@ -15,10 +14,12 @@ namespace Avalonia.Win32
{
internal class Win32StorageProvider : BclStorageProvider
{
private const uint SIGDN_FILESYSPATH = 0x8005 8000 ;
private const uint SIGDN_DESKTOPABSOLUTEPARSING = 0x8002 8000 ;
private const FILEOPENDIALOGOPTIONS DefaultDialogOptions = FILEOPENDIALOGOPTIONS . FOS_FORCEFILESYSTEM | FILEOPENDIALOGOPTIONS . FOS_NOVALIDATE |
FILEOPENDIALOGOPTIONS . FOS_NOTESTFILECREATE | FILEOPENDIALOGOPTIONS . FOS_DONTADDTORECENT ;
private const FILEOPENDIALOGOPTIONS DefaultDialogOptions =
FILEOPENDIALOGOPTIONS . FOS_PATHMUSTEXIST | FILEOPENDIALOGOPTIONS . FOS_FORCEFILESYSTEM |
FILEOPENDIALOGOPTIONS . FOS_NOVALIDATE | FILEOPENDIALOGOPTIONS . FOS_NOTESTFILECREATE |
FILEOPENDIALOGOPTIONS . FOS_DONTADDTORECENT ;
private readonly WindowImpl _ windowImpl ;
@ -35,21 +36,23 @@ namespace Avalonia.Win32
public override async Task < IReadOnlyList < IStorageFolder > > OpenFolderPickerAsync ( FolderPickerOpenOptions options )
{
var files = await ShowFilePicker (
return await ShowFilePicker (
true , true ,
options . AllowMultiple , false ,
options . Title , null , options . SuggestedStartLocation , null , null ) ;
return files . Select ( f = > new BclStorageFolder ( new DirectoryInfo ( f ) ) ) . ToArray ( ) ;
options . Title , null , options . SuggestedStartLocation , null , null ,
f = > new BclStorageFolder ( new DirectoryInfo ( f ) ) )
. ConfigureAwait ( false ) ;
}
public override async Task < IReadOnlyList < IStorageFile > > OpenFilePickerAsync ( FilePickerOpenOptions options )
{
var files = await ShowFilePicker (
return await ShowFilePicker (
true , false ,
options . AllowMultiple , false ,
options . Title , null , options . SuggestedStartLocation ,
null , options . FileTypeFilter ) ;
return files . Select ( f = > new BclStorageFile ( new FileInfo ( f ) ) ) . ToArray ( ) ;
null , options . FileTypeFilter ,
f = > new BclStorageFile ( new FileInfo ( f ) ) )
. ConfigureAwait ( false ) ;
}
public override async Task < IStorageFile ? > SaveFilePickerAsync ( FilePickerSaveOptions options )
@ -58,11 +61,13 @@ namespace Avalonia.Win32
false , false ,
false , options . ShowOverwritePrompt ,
options . Title , options . SuggestedFileName , options . SuggestedStartLocation ,
options . DefaultExtension , options . FileTypeChoices ) ;
return files . Select ( f = > new BclStorageFile ( new FileInfo ( f ) ) ) . FirstOrDefault ( ) ;
options . DefaultExtension , options . FileTypeChoices ,
f = > new BclStorageFile ( new FileInfo ( f ) ) )
. ConfigureAwait ( false ) ;
return files . Count > 0 ? files [ 0 ] : null ;
}
private unsafe Task < IEnumerable < string > > ShowFilePicker (
private unsafe Task < IReadOnlyList < TStorageItem > > ShowFilePicker < TStorageItem > (
bool isOpenFile ,
bool openFolder ,
bool allowMultiple ,
@ -71,11 +76,13 @@ namespace Avalonia.Win32
string? suggestedFileName ,
IStorageFolder ? folder ,
string? defaultExtension ,
IReadOnlyList < FilePickerFileType > ? filters )
IReadOnlyList < FilePickerFileType > ? filters ,
Func < string , TStorageItem > convert )
where TStorageItem : IStorageItem
{
return Task . Run ( ( ) = >
{
IEnumerable < string > result = Array . Empty < string > ( ) ;
IReadOnlyList < TStorageItem > result = Array . Empty < TStorageItem > ( ) ;
try
{
var clsid = isOpenFile ? UnmanagedMethods . ShellIds . OpenFileDialog : UnmanagedMethods . ShellIds . SaveFileDialog ;
@ -101,7 +108,7 @@ namespace Avalonia.Win32
if ( defaultExtension is null )
{
defaultExtension = S tring. Empty ;
defaultExtension = s tring. Empty ;
}
fixed ( char * pExt = defaultExtension )
@ -162,22 +169,22 @@ namespace Avalonia.Win32
var shellItemArray = fileOpenDialog . Results ;
var count = shellItemArray . Count ;
var results = new List < string > ( ) ;
var results = new List < TStorageItem > ( ) ;
for ( int i = 0 ; i < count ; i + + )
{
var shellItem = shellItemArray . GetItemAt ( i ) ;
if ( GetAbsoluteFilePath ( shellItem ) is { } selected )
if ( GetParsingName ( shellItem ) is { } selected )
{
results . Add ( selected ) ;
results . Add ( convert ( selected ) ) ;
}
}
result = results ;
}
else if ( frm . Result is { } shellItem
& & GetAbsoluteFilePath ( shellItem ) is { } singleResult )
& & GetParsingName ( shellItem ) is { } singleResult )
{
result = new [ ] { singleResult } ;
result = new [ ] { convert ( singleResult ) } ;
}
return result ;
@ -191,18 +198,23 @@ namespace Avalonia.Win32
}
private static unsafe string? GetAbsoluteFilePath ( IShellItem shellItem )
private static string? GetParsingName ( IShellItem shellItem )
{
return GetDisplayName ( shellItem , SIGDN_DESKTOPABSOLUTEPARSING ) ;
}
private static unsafe string? GetDisplayName ( IShellItem shellItem , uint sigdnName )
{
var pszString = new IntPtr ( shellItem . GetDisplayName ( SIGDN_FILESYSPATH ) ) ;
if ( pszString ! = IntPtr . Zero )
char * pszString = null ;
if ( shellItem . GetDisplayName ( sigdnName , & pszString ) = = 0 )
{
try
{
return Marshal . PtrToStringUni ( pszString ) ;
return Marshal . PtrToStringUni ( ( IntPtr ) pszString ) ;
}
finally
{
Marshal . FreeCoTaskMem ( pszString ) ;
Marshal . FreeCoTaskMem ( ( IntPtr ) pszString ) ;
}
}
return default ;
@ -224,7 +236,7 @@ namespace Avalonia.Win32
for ( int i = 0 ; i < filters . Count ; i + + )
{
var filter = filters [ i ] ;
if ( filter . Patterns is null | | ! filter . Patterns . Any ( ) )
if ( filter . Patterns is null | | filter . Patterns . Count = = 0 )
{
continue ;
}