diff options
Diffstat (limited to 'widget/windows/filedialog')
-rw-r--r-- | widget/windows/filedialog/WinFileDialogCommands.cpp | 196 | ||||
-rw-r--r-- | widget/windows/filedialog/WinFileDialogCommands.h | 32 | ||||
-rw-r--r-- | widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh | 49 | ||||
-rw-r--r-- | widget/windows/filedialog/moz.build | 22 |
4 files changed, 299 insertions, 0 deletions
diff --git a/widget/windows/filedialog/WinFileDialogCommands.cpp b/widget/windows/filedialog/WinFileDialogCommands.cpp new file mode 100644 index 0000000000..756e6b1cf3 --- /dev/null +++ b/widget/windows/filedialog/WinFileDialogCommands.cpp @@ -0,0 +1,196 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/widget/filedialog/WinFileDialogCommands.h" + +#include <shobjidl.h> +#include <shtypes.h> +#include <winerror.h> +#include "WinUtils.h" + +namespace mozilla::widget::filedialog { + +// Visitor to apply commands to the dialog. +struct Applicator { + IFileDialog* dialog = nullptr; + + HRESULT Visit(Command const& c) { + switch (c.type()) { + default: + case Command::T__None: + return E_INVALIDARG; + + case Command::TSetOptions: + return Apply(c.get_SetOptions()); + case Command::TSetTitle: + return Apply(c.get_SetTitle()); + case Command::TSetOkButtonLabel: + return Apply(c.get_SetOkButtonLabel()); + case Command::TSetFolder: + return Apply(c.get_SetFolder()); + case Command::TSetFileName: + return Apply(c.get_SetFileName()); + case Command::TSetDefaultExtension: + return Apply(c.get_SetDefaultExtension()); + case Command::TSetFileTypes: + return Apply(c.get_SetFileTypes()); + case Command::TSetFileTypeIndex: + return Apply(c.get_SetFileTypeIndex()); + } + } + + HRESULT Apply(SetOptions const& c) { return dialog->SetOptions(c.options()); } + HRESULT Apply(SetTitle const& c) { return dialog->SetTitle(c.title().get()); } + HRESULT Apply(SetOkButtonLabel const& c) { + return dialog->SetOkButtonLabel(c.label().get()); + } + HRESULT Apply(SetFolder const& c) { + RefPtr<IShellItem> folder; + if (SUCCEEDED(SHCreateItemFromParsingName( + c.path().get(), nullptr, IID_IShellItem, getter_AddRefs(folder)))) { + return dialog->SetFolder(folder); + } + // graciously accept that the provided path may have been nonsense + return S_OK; + } + HRESULT Apply(SetFileName const& c) { + return dialog->SetFileName(c.filename().get()); + } + HRESULT Apply(SetDefaultExtension const& c) { + return dialog->SetDefaultExtension(c.extension().get()); + } + HRESULT Apply(SetFileTypes const& c) { + std::vector<COMDLG_FILTERSPEC> vec; + for (auto const& filter : c.filterList()) { + vec.push_back( + {.pszName = filter.name().get(), .pszSpec = filter.spec().get()}); + } + return dialog->SetFileTypes(vec.size(), vec.data()); + } + HRESULT Apply(SetFileTypeIndex const& c) { + return dialog->SetFileTypeIndex(c.index()); + } +}; + +namespace { +static HRESULT GetShellItemPath(IShellItem* aItem, nsString& aResultString) { + NS_ENSURE_TRUE(aItem, E_INVALIDARG); + + LPWSTR str = nullptr; + auto const onExit = MakeScopeExit([&]() { CoTaskMemFree(str); }); + + HRESULT const hr = aItem->GetDisplayName(SIGDN_FILESYSPATH, &str); + if (SUCCEEDED(hr)) { + aResultString.Assign(str); + } + return hr; +} +} // namespace + +#define MOZ_ENSURE_HRESULT_OK(call_) \ + do { \ + HRESULT const hr = (call_); \ + if (FAILED(hr)) return Err(nsresult(hr)); \ + } while (0) + +nsresult ApplyCommands(::IFileDialog* dialog, + nsTArray<Command> const& commands) { + Applicator applicator{.dialog = dialog}; + for (auto const& cmd : commands) { + MOZ_ENSURE_HRESULT_OK(applicator.Visit(cmd)); + } + return NS_OK; +} + +mozilla::Result<Results, nsresult> GetFileResults(::IFileDialog* dialog) { + FILEOPENDIALOGOPTIONS fos; + MOZ_ENSURE_HRESULT_OK(dialog->GetOptions(&fos)); + + using widget::WinUtils; + + // Extract which filter type the user selected + UINT index; + MOZ_ENSURE_HRESULT_OK(dialog->GetFileTypeIndex(&index)); + + // single selection + if ((fos & FOS_ALLOWMULTISELECT) == 0) { + RefPtr<IShellItem> item; + MOZ_ENSURE_HRESULT_OK(dialog->GetResult(getter_AddRefs(item))); + if (!item) { + return Err(nsresult::NS_ERROR_FAILURE); + } + + nsAutoString path; + MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, path)); + + return Results({path}, index); + } + + // multiple selection + RefPtr<IFileOpenDialog> openDlg; + dialog->QueryInterface(IID_IFileOpenDialog, getter_AddRefs(openDlg)); + if (!openDlg) { + MOZ_ASSERT(false, "a file-save dialog was given FOS_ALLOWMULTISELECT?"); + return Err(NS_ERROR_UNEXPECTED); + } + + RefPtr<IShellItemArray> items; + MOZ_ENSURE_HRESULT_OK(openDlg->GetResults(getter_AddRefs(items))); + if (!items) { + return Err(NS_ERROR_FAILURE); + } + + nsTArray<nsString> paths; + + DWORD count = 0; + MOZ_ENSURE_HRESULT_OK(items->GetCount(&count)); + for (DWORD idx = 0; idx < count; idx++) { + RefPtr<IShellItem> item; + MOZ_ENSURE_HRESULT_OK(items->GetItemAt(idx, getter_AddRefs(item))); + + nsAutoString str; + MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, str)); + + paths.EmplaceBack(str); + } + + return Results(std::move(paths), std::move(index)); +} + +mozilla::Result<nsString, nsresult> GetFolderResults(::IFileDialog* dialog) { + RefPtr<IShellItem> item; + MOZ_ENSURE_HRESULT_OK(dialog->GetResult(getter_AddRefs(item))); + if (!item) { + // shouldn't happen -- probably a precondition failure on our part, but + // might be due to misbehaving shell extensions? + MOZ_ASSERT(false, + "unexpected lack of item: was `Show`'s return value checked?"); + return Err(NS_ERROR_FAILURE); + } + + // If the user chose a Win7 Library, resolve to the library's + // default save folder. + RefPtr<IShellLibrary> shellLib; + RefPtr<IShellItem> folderPath; + MOZ_ENSURE_HRESULT_OK( + CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER, + IID_IShellLibrary, getter_AddRefs(shellLib))); + + if (shellLib && SUCCEEDED(shellLib->LoadLibraryFromItem(item, STGM_READ)) && + SUCCEEDED(shellLib->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, + getter_AddRefs(folderPath)))) { + item.swap(folderPath); + } + + // get the folder's file system path + nsAutoString str; + MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, str)); + return str; +} + +#undef MOZ_ENSURE_HRESULT_OK + +} // namespace mozilla::widget::filedialog diff --git a/widget/windows/filedialog/WinFileDialogCommands.h b/widget/windows/filedialog/WinFileDialogCommands.h new file mode 100644 index 0000000000..fcb93eb0f1 --- /dev/null +++ b/widget/windows/filedialog/WinFileDialogCommands.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef widget_windows_filedialog_WinFileDialogCommands_h__ +#define widget_windows_filedialog_WinFileDialogCommands_h__ + +#include "mozilla/widget/filedialog/WinFileDialogCommandsDefn.h" + +// Windows interface type, defined in <shobjidl_core.h> +struct IFileDialog; + +namespace mozilla::widget::filedialog { +// Apply the selected commands to the IFileDialog, in preparation for showing +// it. (The actual showing step is left to the caller.) +[[nodiscard]] nsresult ApplyCommands(::IFileDialog*, + nsTArray<Command> const& commands); + +// Extract one or more results from the file-picker dialog. +// +// Requires that Show() has been called and has returned S_OK. +mozilla::Result<Results, nsresult> GetFileResults(::IFileDialog*); + +// Extract the chosen folder from the folder-picker dialog. +// +// Requires that Show() has been called and has returned S_OK. +mozilla::Result<nsString, nsresult> GetFolderResults(::IFileDialog*); +} // namespace mozilla::widget::filedialog + +#endif // widget_windows_filedialog_WinFileDialogCommands_h__ diff --git a/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh b/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh new file mode 100644 index 0000000000..dd85942f24 --- /dev/null +++ b/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et ft=ipdl : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +namespace mozilla { +namespace widget { +namespace filedialog { + +// Commands corresponding to the various functions in IFileDialog (or at least +// the ones we actually make use of). +// +// All commands' semantics are direct parallels of their equivalently-named +// functions on IFileDialog, with the only changes being those necessary to use +// IPDLable representation-datatypes. (Thus, e.g., `SetOptions` effectively +// takes a `FILEOPENDIALOGOPTIONS`, and `SetFileTypeIndex` is 1-based.) +struct SetOptions { uint32_t options; }; +struct SetTitle { nsString title; }; +struct SetOkButtonLabel { nsString label; }; +struct SetFolder { nsString path; }; +struct SetFileName { nsString filename; }; +struct SetDefaultExtension { nsString extension; }; +struct ComDlgFilterSpec { nsString name; nsString spec; }; +struct SetFileTypes { ComDlgFilterSpec[] filterList; }; +struct SetFileTypeIndex { uint32_t index; }; + +// Union of the above. +union Command { + SetOptions; + SetTitle; + SetOkButtonLabel; + SetFolder; + SetFileName; + SetDefaultExtension; + SetFileTypes; + SetFileTypeIndex; +}; + +// The results from opening a file dialog. (Note that folder selection only +// returns an nsString.) +struct Results { + nsString[] paths; + uint32_t selectedFileTypeIndex; +}; + +} // namespace filedialog +} // namespace widget +} // namespace mozilla diff --git a/widget/windows/filedialog/moz.build b/widget/windows/filedialog/moz.build new file mode 100644 index 0000000000..e6b1b478bb --- /dev/null +++ b/widget/windows/filedialog/moz.build @@ -0,0 +1,22 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +IPDL_SOURCES += [ + "WinFileDialogCommandsDefn.ipdlh", +] + +UNIFIED_SOURCES += [ + "WinFileDialogCommands.cpp", +] + +EXPORTS.mozilla.widget.filedialog += [ + "WinFileDialogCommands.h", +] + +# needed for IPC header files +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" |