#![allow(non_upper_case_globals)]
use winapi::shared::minwindef::*;
use winapi::shared::ntdef::LPWSTR;
use winapi::shared::windef::*;
use winapi::shared::wtypesbase::*;
use winapi::um::combaseapi::*;
use winapi::um::shobjidl::*;
use winapi::um::shobjidl_core::*;
use winapi::um::shtypes::COMDLG_FILTERSPEC;
use winapi::Interface;
use wio::com::ComPtr;
use crate::dialog::{FileDialogOptions, FileDialogType, FileSpec};
use crate::util::{as_result, FromWide, ToWide};
use crate::Error;
use std::ffi::OsString;
use std::ptr::null_mut;
DEFINE_GUID! {CLSID_FileOpenDialog,
0xDC1C_5A9C, 0xE88A, 0x4DDE, 0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}
DEFINE_GUID! {CLSID_FileSaveDialog,
0xC0B4_E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}
unsafe fn make_wstrs(spec: &FileSpec) -> (Vec<u16>, Vec<u16>) {
let exts = spec
.extensions
.iter()
.map(normalize_extension)
.collect::<Vec<_>>();
let name = format!("{} ({})", spec.name, exts.as_slice().join("; ")).to_wide();
let extensions = exts.as_slice().join(";").to_wide();
(name, extensions)
}
fn normalize_extension(ext: &&str) -> String {
format!("*.{}", ext.trim_start_matches('*').trim_start_matches('.'))
}
pub(crate) unsafe fn get_file_dialog_path(
hwnd_owner: HWND,
ty: FileDialogType,
options: FileDialogOptions,
) -> Result<OsString, Error> {
let mut pfd: *mut IFileDialog = null_mut();
let (class, id) = match ty {
FileDialogType::Open => (&CLSID_FileOpenDialog, IFileOpenDialog::uuidof()),
FileDialogType::Save => (&CLSID_FileSaveDialog, IFileSaveDialog::uuidof()),
};
as_result(CoCreateInstance(
class,
null_mut(),
CLSCTX_INPROC_SERVER,
&id,
&mut pfd as *mut *mut IFileDialog as *mut LPVOID,
))?;
let file_dialog = ComPtr::from_raw(pfd);
let mut flags: DWORD = 0;
if options.show_hidden {
flags |= FOS_FORCESHOWHIDDEN;
}
let spec = options.allowed_types.as_ref().map(|allowed_types| {
allowed_types
.iter()
.map(|t| make_wstrs(t))
.collect::<Vec<_>>()
});
let raw_spec = spec.as_ref().map(|buf_pairs| {
buf_pairs
.iter()
.map(|(name, ext)| COMDLG_FILTERSPEC {
pszName: name.as_ptr(),
pszSpec: ext.as_ptr(),
})
.collect::<Vec<_>>()
});
if let Some(spec) = &raw_spec {
as_result(file_dialog.SetFileTypes(spec.len() as u32, spec.as_ptr()))?;
as_result(file_dialog.SetFileTypeIndex(1))?;
}
as_result(file_dialog.SetOptions(flags))?;
as_result(file_dialog.Show(hwnd_owner))?;
let mut result_ptr: *mut IShellItem = null_mut();
as_result(file_dialog.GetResult(&mut result_ptr))?;
let shell_item = ComPtr::from_raw(result_ptr);
let mut display_name: LPWSTR = null_mut();
as_result(shell_item.GetDisplayName(SIGDN_FILESYSPATH, &mut display_name))?;
let filename = display_name.to_os_string();
CoTaskMemFree(display_name as LPVOID);
Ok(filename)
}