Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace the UWP file export with the shell32 one #11365

Merged
5 commits merged into from
Oct 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 36 additions & 21 deletions src/cascadia/TerminalApp/TabManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "ColorHelper.h"
#include "DebugTapConnection.h"
#include "SettingsTab.h"
#include "..\TerminalSettingsModel\FileUtils.h"

#include <shlobj.h>

using namespace winrt;
using namespace winrt::Windows::Foundation::Collections;
Expand Down Expand Up @@ -410,33 +413,45 @@ namespace winrt::TerminalApp::implementation
// - tab: tab to export
winrt::fire_and_forget TerminalPage::_ExportTab(const TerminalTab& tab)
{
// This will be used to set up the file picker "filter", to select .txt
// files by default.
static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
{ L"Text Files (*.txt)", L"*.txt" },
{ L"All Files (*.*)", L"*.*" }
};
// An arbitrary GUID to associate with all instances of this
// dialog, so they all re-open in the same path as they were
// open before:
static constexpr winrt::guid clientGuidExportFile{ 0xF6AF20BB, 0x0800, 0x48E6, { 0xB0, 0x17, 0xA1, 0x4C, 0xD8, 0x73, 0xDD, 0x58 } };

try
{
if (const auto control{ tab.GetActiveTerminalControl() })
{
const FileSavePicker savePicker;
savePicker.as<IInitializeWithWindow>()->Initialize(*_hostingHwnd);
savePicker.SuggestedStartLocation(PickerLocationId::Downloads);
const auto fileChoices = single_threaded_vector<hstring>({ L".txt" });
savePicker.FileTypeChoices().Insert(RS_(L"PlainText"), fileChoices);
savePicker.SuggestedFileName(control.Title());

const StorageFile file = co_await savePicker.PickSaveFileAsync();
if (file != nullptr)
{
const auto buffer = control.ReadEntireBuffer();
CachedFileManager::DeferUpdates(file);
co_await FileIO::WriteTextAsync(file, buffer);
const auto status = co_await CachedFileManager::CompleteUpdatesAsync(file);
switch (status)
// GH#11356 - we can't use the UWP apis for writing the file,
// because they don't work elevated (shocker) So just use the
// shell32 file picker manually.
auto path = co_await SaveFilePicker(*_hostingHwnd, [control](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidExportFile));
try
{
case FileUpdateStatus::Complete:
case FileUpdateStatus::CompleteAndRenamed:
_ShowControlNoticeDialog(RS_(L"NoticeInfo"), RS_(L"ExportSuccess"));
break;
default:
_ShowControlNoticeDialog(RS_(L"NoticeError"), RS_(L"ExportFailure"));
// Default to the Downloads folder
auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_Downloads, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(folderShellItem.get());
}
CATCH_LOG(); // non-fatal
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedFileTypes), supportedFileTypes));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::size is the modern ARRAYSIZE.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, to think i've been using std::extent<decltype(x)>::value all this time. thanks!

THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"txt"));

// Default to using the tab title as the file name
THROW_IF_FAILED(dialog->SetFileName((control.Title() + L".txt").c_str()));
});

if (!path.empty())
{
const auto buffer = control.ReadEntireBuffer();
CascadiaSettings::ExportFile(path, buffer);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalSettingsEditor/Appearances.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
Expand All @@ -7,6 +7,7 @@
#include "EnumEntry.h"

#include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"

using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml;
Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalSettingsEditor/Profiles.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
Expand All @@ -8,6 +8,7 @@
#include "EnumEntry.h"

#include <LibraryResources.h>
#include "..\WinRTUtils\inc\Utils.h"

using namespace winrt::Windows::UI::Text;
using namespace winrt::Windows::UI::Xaml;
Expand Down
24 changes: 0 additions & 24 deletions src/cascadia/TerminalSettingsEditor/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,6 @@ using namespace winrt::Windows::UI::Xaml;

UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"Microsoft.Terminal.Settings.Editor/Resources");

// Function Description:
// - Helper that opens a file picker pre-seeded with image file types.
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd)
{
static constexpr COMDLG_FILTERSPEC supportedImageFileTypes[] = {
{ L"All Supported Bitmap Types (*.jpg, *.jpeg, *.png, *.bmp, *.gif, *.tiff, *.ico)", L"*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.ico" },
{ L"All Files (*.*)", L"*.*" }
};

static constexpr winrt::guid clientGuidImagePicker{ 0x55675F54, 0x74A1, 0x4552, { 0xA3, 0x9D, 0x94, 0xAE, 0x85, 0xD8, 0xF2, 0x7A } };
return OpenFilePicker(parentHwnd, [](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidImagePicker));
try
{
auto pictureFolderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_PicturesLibrary, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(pictureFolderShellItem.get());
}
CATCH_LOG(); // non-fatal
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedImageFileTypes), supportedImageFileTypes));
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"jpg;jpeg;png;bmp;gif;tiff;ico"));
});
}

namespace winrt::Microsoft::Terminal::Settings
{
hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable)
Expand Down
36 changes: 0 additions & 36 deletions src/cascadia/TerminalSettingsEditor/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,42 +97,6 @@ public: \
private: \
static winrt::Windows::UI::Xaml::DependencyProperty _##name##Property;

// Function Description:
// - This function presents a File Open "common dialog" and returns its selected file asynchronously.
// Parameters:
// - customize: A lambda that receives an IFileDialog* to customize.
// Return value:
// (async) path to the selected item.
template<typename TLambda>
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenFilePicker(HWND parentHwnd, TLambda&& customize)
{
auto fileDialog{ winrt::create_instance<IFileDialog>(CLSID_FileOpenDialog) };
DWORD flags{};
THROW_IF_FAILED(fileDialog->GetOptions(&flags));
THROW_IF_FAILED(fileDialog->SetOptions(flags | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_DONTADDTORECENT)); // filesystem objects only; no recent places
customize(fileDialog.get());

auto hr{ fileDialog->Show(parentHwnd) };
if (!SUCCEEDED(hr))
{
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
{
co_return winrt::hstring{};
}
THROW_HR(hr);
}

winrt::com_ptr<IShellItem> result;
THROW_IF_FAILED(fileDialog->GetResult(result.put()));

wil::unique_cotaskmem_string filePath;
THROW_IF_FAILED(result->GetDisplayName(SIGDN_FILESYSPATH, &filePath));

co_return winrt::hstring{ filePath.get() };
}

winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd);

namespace winrt::Microsoft::Terminal::Settings
{
winrt::hstring GetSelectedItemTag(winrt::Windows::Foundation::IInspectable const& comboBoxAsInspectable);
Expand Down
11 changes: 11 additions & 0 deletions src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "CascadiaSettings.h"
#include "CascadiaSettings.g.cpp"

#include "FileUtils.h"

#include <LibraryResources.h>
#include <VersionHelpers.h>

Expand Down Expand Up @@ -880,3 +882,12 @@ void CascadiaSettings::CurrentDefaultTerminal(const Model::DefaultTerminal& term
{
_currentDefaultTerminal = terminal;
}

void CascadiaSettings::ExportFile(winrt::hstring path, winrt::hstring content)
{
try
{
winrt::Microsoft::Terminal::Settings::Model::WriteUTF8FileAtomic({ path.c_str() }, til::u16u8(content));
}
CATCH_LOG();
}
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/CascadiaSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
static winrt::hstring DefaultSettingsPath();
static winrt::hstring ApplicationDisplayName();
static winrt::hstring ApplicationVersion();
static void ExportFile(winrt::hstring path, winrt::hstring content);

CascadiaSettings() noexcept = default;
CascadiaSettings(const winrt::hstring& userJSON, const winrt::hstring& inboxJSON);
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalSettingsModel/CascadiaSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace Microsoft.Terminal.Settings.Model

static String ApplicationDisplayName { get; };
static String ApplicationVersion { get; };

static void ExportFile(String path, String content);

CascadiaSettings(String userJSON, String inboxJSON);

Expand Down
33 changes: 33 additions & 0 deletions src/cascadia/WinRTUtils/Utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

#include "pch.h"
#include "Utils.h"

// Function Description:
// - Helper that opens a file picker pre-seeded with image file types.
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd)
{
static constexpr COMDLG_FILTERSPEC supportedImageFileTypes[] = {
{ L"All Supported Bitmap Types (*.jpg, *.jpeg, *.png, *.bmp, *.gif, *.tiff, *.ico)", L"*.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.ico" },
{ L"All Files (*.*)", L"*.*" }
};

static constexpr winrt::guid clientGuidImagePicker{ 0x55675F54, 0x74A1, 0x4552, { 0xA3, 0x9D, 0x94, 0xAE, 0x85, 0xD8, 0xF2, 0x7A } };
return OpenFilePicker(parentHwnd, [](auto&& dialog) {
THROW_IF_FAILED(dialog->SetClientGuid(clientGuidImagePicker));
try
{
auto pictureFolderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_PicturesLibrary, KF_FLAG_DEFAULT, nullptr) };
dialog->SetDefaultFolder(pictureFolderShellItem.get());
}
CATCH_LOG(); // non-fatal

#pragma warning(suppress : 26485) // so we can pass in the supportedImageFileTypes without the analyzer complaining
THROW_IF_FAILED(dialog->SetFileTypes(ARRAYSIZE(supportedImageFileTypes), supportedImageFileTypes));
THROW_IF_FAILED(dialog->SetFileTypeIndex(1)); // the array is 1-indexed
THROW_IF_FAILED(dialog->SetDefaultExtension(L"jpg;jpeg;png;bmp;gif;tiff;ico"));
});
}
1 change: 1 addition & 0 deletions src/cascadia/WinRTUtils/WinRTUtils.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
</ClCompile>
<ClCompile Include="LibraryResources.cpp" />
<ClCompile Include="ScopedResourceLoader.cpp" />
<ClCompile Include="Utils.cpp" />
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
Expand Down
48 changes: 48 additions & 0 deletions src/cascadia/WinRTUtils/inc/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,51 @@ inline winrt::Windows::Foundation::Rect ScaleRect(winrt::Windows::Foundation::Re
rect.Height = base::ClampMul(rect.Height, scaleLocal);
return rect;
}

// Function Description:
// - This function presents a File Open "common dialog" and returns its selected file asynchronously.
// Parameters:
// - customize: A lambda that receives an IFileDialog* to customize.
// Return value:
// (async) path to the selected item.
template<typename TLambda>
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> FilePicker(HWND parentHwnd, bool saveDialog, TLambda&& customize)
{
auto fileDialog{ saveDialog ? winrt::create_instance<IFileDialog>(CLSID_FileSaveDialog) :
winrt::create_instance<IFileDialog>(CLSID_FileOpenDialog) };
DWORD flags{};
THROW_IF_FAILED(fileDialog->GetOptions(&flags));
THROW_IF_FAILED(fileDialog->SetOptions(flags | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_DONTADDTORECENT)); // filesystem objects only; no recent places
customize(fileDialog.get());

const auto hr{ fileDialog->Show(parentHwnd) };
if (!SUCCEEDED(hr))
{
if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
{
co_return winrt::hstring{};
}
THROW_HR(hr);
}

winrt::com_ptr<IShellItem> result;
THROW_IF_FAILED(fileDialog->GetResult(result.put()));

wil::unique_cotaskmem_string filePath;
THROW_IF_FAILED(result->GetDisplayName(SIGDN_FILESYSPATH, &filePath));

co_return winrt::hstring{ filePath.get() };
}

template<typename TLambda>
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenFilePicker(HWND parentHwnd, TLambda&& customize)
{
return FilePicker(parentHwnd, false, customize);
}
template<typename TLambda>
winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> SaveFilePicker(HWND parentHwnd, TLambda&& customize)
{
return FilePicker(parentHwnd, true, customize);
}

winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker(HWND parentHwnd);
5 changes: 4 additions & 1 deletion src/cascadia/WinRTUtils/pch.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// pch.h
Expand Down Expand Up @@ -31,3 +31,6 @@
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.System.h>

#include <shlobj.h>
#include <shobjidl_core.h>