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

Conhost: copy RTF to clipboard #3595

Merged
3 commits merged into from
Dec 5, 2019
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
85 changes: 50 additions & 35 deletions src/interactivity/win32/Clipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ using namespace Microsoft::Console::Types;
#pragma region Public Methods

// Arguments:
// - fAlsoCopyHtml - Place colored HTML text onto the clipboard as well as the usual plain text.
// - fAlsoCopyFormatting - Place colored HTML & RTF text onto the clipboard as well as the usual plain text.
// Return Value:
// <none>
// NOTE: if the registry is set to always copy color data then we will even if fAlsoCopyHTML is false
void Clipboard::Copy(bool fAlsoCopyHtml)
// NOTE: if the registry is set to always copy color data then we will even if fAlsoCopyFormatting is false
void Clipboard::Copy(bool fAlsoCopyFormatting)
{
try
{
// registry settings may tell us to always copy the color/formating
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
fAlsoCopyHtml = fAlsoCopyHtml || gci.GetCopyColor();
fAlsoCopyFormatting = fAlsoCopyFormatting || gci.GetCopyColor();

// store selection in clipboard
StoreSelectionToClipboard(fAlsoCopyHtml);
StoreSelectionToClipboard(fAlsoCopyFormatting);
Selection::Instance().ClearSelection(); // clear selection in console
}
CATCH_LOG();
Expand Down Expand Up @@ -188,10 +188,10 @@ std::deque<std::unique_ptr<IInputEvent>> Clipboard::TextToKeyEvents(_In_reads_(c
// - Copies the selected area onto the global system clipboard.
// - NOTE: Throws on allocation and other clipboard failures.
// Arguments:
// - fAlsoCopyHtml - This will also place colored HTML text onto the clipboard as well as the usual plain text.
// - fAlsoCopyFormatting - This will also place colored HTML & RTF text onto the clipboard as well as the usual plain text.
// Return Value:
// <none>
void Clipboard::StoreSelectionToClipboard(bool const fAlsoCopyHtml)
void Clipboard::StoreSelectionToClipboard(bool const fAlsoCopyFormatting)
{
const auto& selection = Selection::Instance();

Expand All @@ -212,7 +212,7 @@ void Clipboard::StoreSelectionToClipboard(bool const fAlsoCopyHtml)
lineSelection,
selectionRects);

CopyTextToSystemClipboard(text, fAlsoCopyHtml);
CopyTextToSystemClipboard(text, fAlsoCopyFormatting);
}

// Routine Description:
Expand Down Expand Up @@ -243,7 +243,8 @@ TextBuffer::TextAndColor Clipboard::RetrieveTextFromBuffer(const SCREEN_INFORMAT
// - Copies the text given onto the global system clipboard.
// Arguments:
// - rows - Rows of text data to copy
void Clipboard::CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyHtml)
// - fAlsoCopyFormatting - true if the color and formatting should also be copied, false otherwise
void Clipboard::CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, bool const fAlsoCopyFormatting)
{
std::wstring finalString;

Expand Down Expand Up @@ -279,37 +280,17 @@ void Clipboard::CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows,
THROW_LAST_ERROR_IF(!EmptyClipboard());
THROW_LAST_ERROR_IF_NULL(SetClipboardData(CF_UNICODETEXT, globalHandle.get()));

if (fAlsoCopyHtml)
if (fAlsoCopyFormatting)
{
const auto& fontData = ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetCurrentFont();
int const iFontHeightPoints = fontData.GetUnscaledSize().Y * 72 / ServiceLocator::LocateGlobals().dpi;
const COLORREF bgColor = ServiceLocator::LocateGlobals().getConsoleInformation().GetDefaultBackground();

std::string HTMLToPlaceOnClip = TextBuffer::GenHTML(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor, "Windows Console Host");
const size_t cbNeededHTML = HTMLToPlaceOnClip.size() + 1;
if (cbNeededHTML)
{
wil::unique_hglobal globalHandleHTML(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbNeededHTML));
THROW_LAST_ERROR_IF_NULL(globalHandleHTML.get());

PSTR pszClipboardHTML = (PSTR)GlobalLock(globalHandleHTML.get());
THROW_LAST_ERROR_IF_NULL(pszClipboardHTML);

// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr2 = StringCchCopyA(pszClipboardHTML, cbNeededHTML, HTMLToPlaceOnClip.data());
GlobalUnlock(globalHandleHTML.get());
THROW_IF_FAILED(hr2);

UINT const CF_HTML = RegisterClipboardFormatW(L"HTML Format");
THROW_LAST_ERROR_IF(0 == CF_HTML);

THROW_LAST_ERROR_IF_NULL(SetClipboardData(CF_HTML, globalHandleHTML.get()));

// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandleHTML.release();
}
CopyToSystemClipboard(HTMLToPlaceOnClip, L"HTML Format");

std::string RTFToPlaceOnClip = TextBuffer::GenRTF(rows, iFontHeightPoints, fontData.GetFaceName(), bgColor);
CopyToSystemClipboard(RTFToPlaceOnClip, L"Rich Text Format");
}
}

Expand All @@ -319,6 +300,40 @@ void Clipboard::CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows,
globalHandle.release();
}

// Routine Description:
// - Copies the given string onto the global system clipboard in the sepcified format
// Arguments:
// - stringToCopy - The string to copy
// - lpszFormat - the name of the format
void Clipboard::CopyToSystemClipboard(std::string stringToCopy, LPCWSTR lpszFormat)
{
const size_t cbData = stringToCopy.size() + 1; // +1 for '\0'
if (cbData)
{
wil::unique_hglobal globalHandleData(GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, cbData));
THROW_LAST_ERROR_IF_NULL(globalHandleData.get());

PSTR pszClipboardHTML = (PSTR)GlobalLock(globalHandleData.get());
THROW_LAST_ERROR_IF_NULL(pszClipboardHTML);

// The pattern gets a bit strange here because there's no good wil built-in for global lock of this type.
// Try to copy then immediately unlock. Don't throw until after (so the hglobal won't be freed until we unlock).
const HRESULT hr2 = StringCchCopyA(pszClipboardHTML, cbData, stringToCopy.data());
GlobalUnlock(globalHandleData.get());
THROW_IF_FAILED(hr2);

UINT const CF_FORMAT = RegisterClipboardFormatW(lpszFormat);
THROW_LAST_ERROR_IF(0 == CF_FORMAT);

THROW_LAST_ERROR_IF_NULL(SetClipboardData(CF_FORMAT, globalHandleData.get()));

// only free if we failed.
// the memory has to remain allocated if we successfully placed it on the clipboard.
// Releasing the smart pointer will leave it allocated as we exit scope.
globalHandleData.release();
}
}

// Returns true if the character should be emitted to the paste stream
// -- in some cases, we will change what character should be emitted, as in the case of "smart quotes"
// Returns false if the character should not be emitted (e.g. <TAB>)
Expand Down
8 changes: 4 additions & 4 deletions src/interactivity/win32/clipboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Microsoft::Console::Interactivity::Win32
public:
static Clipboard& Instance();

void Copy(_In_ bool const fAlsoCopyHtml = false);
void Copy(_In_ bool const fAlsoCopyFormatting = false);
void StringPaste(_In_reads_(cchData) PCWCHAR pwchData,
const size_t cchData);
void Paste();
Expand All @@ -38,14 +38,14 @@ namespace Microsoft::Console::Interactivity::Win32
std::deque<std::unique_ptr<IInputEvent>> TextToKeyEvents(_In_reads_(cchData) const wchar_t* const pData,
const size_t cchData);

void StoreSelectionToClipboard(_In_ bool const fAlsoCopyHtml);
void StoreSelectionToClipboard(_In_ bool const fAlsoCopyFormatting);

TextBuffer::TextAndColor RetrieveTextFromBuffer(const SCREEN_INFORMATION& screenInfo,
const bool lineSelection,
const std::vector<SMALL_RECT>& selectionRects);

void CopyHTMLToClipboard(const TextBuffer::TextAndColor& rows);
void CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, _In_ bool const fAlsoCopyHtml);
void CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, _In_ bool const fAlsoCopyFormatting);
void CopyToSystemClipboard(std::string stringToPlaceOnClip, LPCWSTR lpszFormat);

bool FilterCharacterOnPaste(_Inout_ WCHAR* const pwch);

Expand Down
4 changes: 2 additions & 2 deletions src/interactivity/win32/windowio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,8 +800,8 @@ BOOL HandleMouseEvent(const SCREEN_INFORMATION& ScreenInfo,
Telemetry::Instance().LogQuickEditCopyRawUsed();
}
// If the ALT key is held, also select HTML as well as plain text.
bool const fAlsoSelectHtml = WI_IsFlagSet(GetKeyState(VK_MENU), KEY_PRESSED);
Clipboard::Instance().Copy(fAlsoSelectHtml);
bool const fAlsoCopyFormatting = WI_IsFlagSet(GetKeyState(VK_MENU), KEY_PRESSED);
Clipboard::Instance().Copy(fAlsoCopyFormatting);
}
else if (gci.Flags & CONSOLE_QUICK_EDIT_MODE)
{
Expand Down