Fix Encoding "Convert to..." regression

Fix incorrect Clipboard handling. This commit fixes possibly #9426

Fix #15324, fix #15271, fix #3054, close #15346
pull/15371/head
xomx 2024-06-24 00:49:50 +02:00 committed by Don Ho
parent 53d87d4f62
commit 3c3e7cdadd
8 changed files with 245 additions and 137 deletions

View File

@ -858,7 +858,6 @@ bool str2Clipboard(const generic_string &str2cpy, HWND hwnd)
if (!::OpenClipboard(hwnd)) if (!::OpenClipboard(hwnd))
{ {
::GlobalFree(hglbCopy); ::GlobalFree(hglbCopy);
::CloseClipboard();
return false; return false;
} }
if (!::EmptyClipboard()) if (!::EmptyClipboard())
@ -869,9 +868,8 @@ bool str2Clipboard(const generic_string &str2cpy, HWND hwnd)
} }
// Lock the handle and copy the text to the buffer. // Lock the handle and copy the text to the buffer.
TCHAR *pStr = (TCHAR *)::GlobalLock(hglbCopy); TCHAR *pStr = (TCHAR *)::GlobalLock(hglbCopy);
if (pStr == NULL) if (!pStr)
{ {
::GlobalUnlock(hglbCopy);
::GlobalFree(hglbCopy); ::GlobalFree(hglbCopy);
::CloseClipboard(); ::CloseClipboard();
return false; return false;
@ -880,7 +878,7 @@ bool str2Clipboard(const generic_string &str2cpy, HWND hwnd)
::GlobalUnlock(hglbCopy); ::GlobalUnlock(hglbCopy);
// Place the handle on the clipboard. // Place the handle on the clipboard.
unsigned int clipBoardFormat = CF_UNICODETEXT; unsigned int clipBoardFormat = CF_UNICODETEXT;
if (::SetClipboardData(clipBoardFormat, hglbCopy) == NULL) if (!::SetClipboardData(clipBoardFormat, hglbCopy))
{ {
::GlobalFree(hglbCopy); ::GlobalFree(hglbCopy);
::CloseClipboard(); ::CloseClipboard();

View File

@ -2870,19 +2870,29 @@ void Notepad_plus::pasteToMarkedLines()
{ {
std::lock_guard<std::mutex> lock(mark_mutex); std::lock_guard<std::mutex> lock(mark_mutex);
int clipFormat; unsigned int clipFormat = CF_UNICODETEXT;
clipFormat = CF_UNICODETEXT;
BOOL canPaste = ::IsClipboardFormatAvailable(clipFormat); if (!::IsClipboardFormatAvailable(clipFormat))
if (!canPaste)
return; return;
intptr_t lastLine = _pEditView->lastZeroBasedLineNumber(); intptr_t lastLine = _pEditView->lastZeroBasedLineNumber();
::OpenClipboard(_pPublicInterface->getHSelf()); if (!::OpenClipboard(_pPublicInterface->getHSelf()))
return;
HANDLE clipboardData = ::GetClipboardData(clipFormat); HANDLE clipboardData = ::GetClipboardData(clipFormat);
::GlobalSize(clipboardData); if (!clipboardData)
{
::CloseClipboard();
return;
}
LPVOID clipboardDataPtr = ::GlobalLock(clipboardData); LPVOID clipboardDataPtr = ::GlobalLock(clipboardData);
if (!clipboardDataPtr) return; if (!clipboardDataPtr)
{
::CloseClipboard();
return;
}
generic_string clipboardStr = (const TCHAR *)clipboardDataPtr; generic_string clipboardStr = (const TCHAR *)clipboardDataPtr;

View File

@ -427,51 +427,70 @@ void Notepad_plus::command(int id)
char *pBinText = new char[textLen + 1]; char *pBinText = new char[textLen + 1];
_pEditView->getSelectedText(pBinText, textLen + 1); _pEditView->getSelectedText(pBinText, textLen + 1);
// Open the clipboard, and empty it. // Open the clipboard and empty it.
if (!OpenClipboard(NULL)) if (!::OpenClipboard(NULL))
return; return;
EmptyClipboard(); if (!::EmptyClipboard())
{
::CloseClipboard();
return;
}
// Allocate a global memory object for the text. // Allocate a global memory object for the text.
HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (textLen + 1) * sizeof(unsigned char)); HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, (textLen + 1) * sizeof(unsigned char));
if (hglbCopy == NULL) if (!hglbCopy)
{ {
CloseClipboard(); ::CloseClipboard();
return; return;
} }
// Lock the handle and copy the text to the buffer. // Lock the handle and copy the text to the buffer.
unsigned char *lpucharCopy = (unsigned char *)GlobalLock(hglbCopy); unsigned char *lpucharCopy = (unsigned char *)::GlobalLock(hglbCopy);
if (!lpucharCopy)
{
::GlobalFree(hglbCopy);
::CloseClipboard();
return;
}
memcpy(lpucharCopy, pBinText, textLen * sizeof(unsigned char)); memcpy(lpucharCopy, pBinText, textLen * sizeof(unsigned char));
lpucharCopy[textLen] = 0; // null character lpucharCopy[textLen] = 0; // null character
delete[] pBinText; delete[] pBinText;
GlobalUnlock(hglbCopy); ::GlobalUnlock(hglbCopy);
// Place the handle on the clipboard. // Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy); if (!::SetClipboardData(CF_TEXT, hglbCopy))
{
::GlobalFree(hglbCopy);
::CloseClipboard();
return;
}
// Allocate a global memory object for the text length. // Allocate a global memory object for the text length.
HGLOBAL hglbLenCopy = GlobalAlloc(GMEM_MOVEABLE, sizeof(unsigned long)); HGLOBAL hglbLenCopy = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(unsigned long));
if (hglbLenCopy == NULL) if (!hglbLenCopy)
{ {
CloseClipboard(); ::CloseClipboard();
return; return;
} }
// Lock the handle and copy the text to the buffer. // Lock the handle and copy the text to the buffer.
unsigned long *lpLenCopy = (unsigned long *)GlobalLock(hglbLenCopy); unsigned long *lpLenCopy = (unsigned long *)::GlobalLock(hglbLenCopy);
if (!lpLenCopy)
{
::CloseClipboard();
return;
}
*lpLenCopy = static_cast<unsigned long>(textLen); *lpLenCopy = static_cast<unsigned long>(textLen);
GlobalUnlock(hglbLenCopy); ::GlobalUnlock(hglbLenCopy);
// Place the handle on the clipboard. // Place the handle on the clipboard.
UINT cf_nppTextLen = RegisterClipboardFormat(CF_NPPTEXTLEN); UINT cf_nppTextLen = ::RegisterClipboardFormat(CF_NPPTEXTLEN);
SetClipboardData(cf_nppTextLen, hglbLenCopy); ::SetClipboardData(cf_nppTextLen, hglbLenCopy);
CloseClipboard(); ::CloseClipboard();
if (id == IDM_EDIT_CUT_BINARY) if (id == IDM_EDIT_CUT_BINARY)
_pEditView->execute(SCI_REPLACESEL, 0, reinterpret_cast<LPARAM>("")); _pEditView->execute(SCI_REPLACESEL, 0, reinterpret_cast<LPARAM>(""));
@ -3122,44 +3141,95 @@ void Notepad_plus::command(int id)
if (idEncoding != -1) if (idEncoding != -1)
{ {
// Save the current clipboard content // try to save the current clipboard CF_TEXT content 1st
::OpenClipboard(_pPublicInterface->getHSelf()); HGLOBAL hglbClipboardCopy = NULL;
HANDLE clipboardData = ::GetClipboardData(CF_TEXT); if (::OpenClipboard(_pPublicInterface->getHSelf()))
LPVOID clipboardData2 = NULL;
if (clipboardData != NULL)
{ {
int len = static_cast<int32_t>(::GlobalSize(clipboardData)); HANDLE hClipboardData = ::GetClipboardData(CF_TEXT);
LPVOID clipboardDataPtr = ::GlobalLock(clipboardData); if (hClipboardData) // NULL if there is no previous CF_TEXT data in
{
HANDLE allocClipboardData = ::GlobalAlloc(GMEM_MOVEABLE, len); LPVOID pClipboardData = ::GlobalLock(hClipboardData);
clipboardData2 = ::GlobalLock(allocClipboardData); if (pClipboardData)
{
::memcpy(clipboardData2, clipboardDataPtr, len); size_t clipboardDataSize = ::GlobalSize(pClipboardData);
::GlobalUnlock(clipboardData); hglbClipboardCopy = ::GlobalAlloc(GMEM_MOVEABLE, clipboardDataSize);
::GlobalUnlock(allocClipboardData); if (hglbClipboardCopy)
{
LPVOID pClipboardCopy = ::GlobalLock(hglbClipboardCopy);
if (pClipboardCopy)
{
::memcpy(pClipboardCopy, pClipboardData, clipboardDataSize);
::GlobalUnlock(hglbClipboardCopy);
}
else
{
::GlobalFree(hglbClipboardCopy);
hglbClipboardCopy = NULL;
}
}
::GlobalUnlock(hClipboardData);
}
}
::CloseClipboard(); ::CloseClipboard();
} }
_pEditView->saveCurrentPos(); _pEditView->saveCurrentPos();
bool bPreviousCHPanelTrackingState = true;
if (_pClipboardHistoryPanel)
bPreviousCHPanelTrackingState = _pClipboardHistoryPanel->trackClipboardOps(false); // we do not want to track & show the next Clipboard op
// Cut all text // Cut all text
size_t docLen = _pEditView->getCurrentDocLen(); size_t docLen = _pEditView->getCurrentDocLen();
_pEditView->execute(SCI_COPYRANGE, 0, docLen); _pEditView->execute(SCI_COPYRANGE, 0, docLen);
_pEditView->execute(SCI_CLEARALL); _pEditView->execute(SCI_CLEARALL);
if (_pClipboardHistoryPanel)
_pClipboardHistoryPanel->trackClipboardOps(bPreviousCHPanelTrackingState); // restore
// Change to the proper buffer, save buffer status // Change to the proper buffer, save buffer status
::SendMessage(_pPublicInterface->getHSelf(), WM_COMMAND, idEncoding, 0); ::SendMessage(_pPublicInterface->getHSelf(), WM_COMMAND, idEncoding, 0);
// Paste the texte, restore buffer status // Paste the text, restore buffer status
_pEditView->execute(SCI_PASTE); _pEditView->execute(SCI_PASTE);
_pEditView->restoreCurrentPosPreStep(); _pEditView->restoreCurrentPosPreStep();
// Restore the previous clipboard data // Restore the previous Clipboard data if any
::OpenClipboard(_pPublicInterface->getHSelf()); if (hglbClipboardCopy)
::EmptyClipboard(); {
::SetClipboardData(CF_TEXT, clipboardData2); bool bAllOk = false;
::CloseClipboard(); if (::OpenClipboard(_pPublicInterface->getHSelf()))
{
LPVOID pClipboardCopy = ::GlobalLock(hglbClipboardCopy);
if (pClipboardCopy)
{
if (::EmptyClipboard())
{
if (::SetClipboardData(CF_TEXT, pClipboardCopy))
bAllOk = true;
}
::GlobalUnlock(hglbClipboardCopy);
}
::CloseClipboard();
}
if (!bAllOk)
{
// when we failed to pass the data back to the Clipboard,
// we have to free our copy here otherwise there will be memory leak
::GlobalFree(hglbClipboardCopy);
hglbClipboardCopy = NULL;
}
}
else
{
// no previous Clipboard data, clear the ones used by the Scintilla's conversion
if (::OpenClipboard(_pPublicInterface->getHSelf()))
{
::EmptyClipboard();
::CloseClipboard();
}
}
//Do not free anything, EmptyClipboard does that //Do not free anything, EmptyClipboard does that
_pEditView->execute(SCI_EMPTYUNDOBUFFER); _pEditView->execute(SCI_EMPTYUNDOBUFFER);

View File

@ -4591,68 +4591,77 @@ bool ScintillaEditView::pasteToMultiSelection() const
// "MSDEVColumnSelect" is column format from Scintilla // "MSDEVColumnSelect" is column format from Scintilla
CLIPFORMAT cfColumnSelect = static_cast<CLIPFORMAT>(::RegisterClipboardFormat(TEXT("MSDEVColumnSelect"))); CLIPFORMAT cfColumnSelect = static_cast<CLIPFORMAT>(::RegisterClipboardFormat(TEXT("MSDEVColumnSelect")));
if (IsClipboardFormatAvailable(cfColumnSelect) && OpenClipboard(NULL)) if (!::IsClipboardFormatAvailable(cfColumnSelect) || !::OpenClipboard(NULL))
return false;
HANDLE clipboardData = ::GetClipboardData(CF_UNICODETEXT);
if (!clipboardData)
{ {
HANDLE clipboardData = ::GetClipboardData(CF_UNICODETEXT); ::CloseClipboard();
::GlobalSize(clipboardData); return false;
LPVOID clipboardDataPtr = ::GlobalLock(clipboardData);
if (clipboardDataPtr)
{
wstring clipboardStr = (const TCHAR*)clipboardDataPtr;
::GlobalUnlock(clipboardData);
::CloseClipboard();
vector<wstring> clipboardStrings;
stringSplit(clipboardStr, getEOLString(), clipboardStrings);
clipboardStrings.erase(clipboardStrings.cend() - 1); // remove the last empty string
size_t nbClipboardStr = clipboardStrings.size();
if (nbSelections >= nbClipboardStr) // enough holes for every insertion, keep holes empty if there are some left
{
execute(SCI_BEGINUNDOACTION);
for (size_t i = 0; i < nbClipboardStr; ++i)
{
LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i);
LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i);
replaceTarget(clipboardStrings[i].c_str(), posStart, posEnd);
posStart += clipboardStrings[i].length();
execute(SCI_SETSELECTIONNSTART, i, posStart);
execute(SCI_SETSELECTIONNEND, i, posStart);
}
execute(SCI_ENDUNDOACTION);
return true;
}
else if (nbSelections < nbClipboardStr) // not enough holes for insertion, every hole has several insertions
{
size_t nbStr2takeFromClipboard = nbClipboardStr / nbSelections;
execute(SCI_BEGINUNDOACTION);
size_t j = 0;
for (size_t i = 0; i < nbSelections; ++i)
{
LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i);
LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i);
wstring severalStr;
wstring eol = getEOLString();
for (size_t k = 0; k < nbStr2takeFromClipboard && j < nbClipboardStr; ++k)
{
severalStr += clipboardStrings[j];
severalStr += eol;
++j;
}
// remove the latest added EOL
severalStr.erase(severalStr.length() - eol.length());
replaceTarget(severalStr.c_str(), posStart, posEnd);
posStart += severalStr.length();
execute(SCI_SETSELECTIONNSTART, i, posStart);
execute(SCI_SETSELECTIONNEND, i, posStart);
}
execute(SCI_ENDUNDOACTION);
return true;
}
}
} }
LPVOID clipboardDataPtr = ::GlobalLock(clipboardData);
if (!clipboardDataPtr)
{
::CloseClipboard();
return false;
}
wstring clipboardStr = static_cast<const TCHAR*>(clipboardDataPtr);
::GlobalUnlock(clipboardData);
::CloseClipboard();
vector<wstring> clipboardStrings;
stringSplit(clipboardStr, getEOLString(), clipboardStrings);
clipboardStrings.erase(clipboardStrings.cend() - 1); // remove the last empty string
size_t nbClipboardStr = clipboardStrings.size();
if (nbSelections >= nbClipboardStr) // enough holes for every insertion, keep holes empty if there are some left
{
execute(SCI_BEGINUNDOACTION);
for (size_t i = 0; i < nbClipboardStr; ++i)
{
LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i);
LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i);
replaceTarget(clipboardStrings[i].c_str(), posStart, posEnd);
posStart += clipboardStrings[i].length();
execute(SCI_SETSELECTIONNSTART, i, posStart);
execute(SCI_SETSELECTIONNEND, i, posStart);
}
execute(SCI_ENDUNDOACTION);
return true;
}
else if (nbSelections < nbClipboardStr) // not enough holes for insertion, every hole has several insertions
{
size_t nbStr2takeFromClipboard = nbClipboardStr / nbSelections;
execute(SCI_BEGINUNDOACTION);
size_t j = 0;
for (size_t i = 0; i < nbSelections; ++i)
{
LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i);
LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i);
wstring severalStr;
wstring eol = getEOLString();
for (size_t k = 0; k < nbStr2takeFromClipboard && j < nbClipboardStr; ++k)
{
severalStr += clipboardStrings[j];
severalStr += eol;
++j;
}
// remove the latest added EOL
severalStr.erase(severalStr.length() - eol.length());
replaceTarget(severalStr.c_str(), posStart, posEnd);
posStart += severalStr.length();
execute(SCI_SETSELECTIONNSTART, i, posStart);
execute(SCI_SETSELECTIONNEND, i, posStart);
}
execute(SCI_ENDUNDOACTION);
return true;
}
return false; return false;
} }

View File

@ -1734,22 +1734,26 @@ bool StringDlg::isAllowed([[maybe_unused]] const generic_string & txt)
void StringDlg::HandlePaste(HWND hEdit) void StringDlg::HandlePaste(HWND hEdit)
{ {
if (OpenClipboard(hEdit)) if (!::OpenClipboard(hEdit))
return;
HANDLE hClipboardData = ::GetClipboardData(CF_UNICODETEXT);
if (!hClipboardData)
{ {
HANDLE hClipboardData = GetClipboardData(CF_UNICODETEXT); ::CloseClipboard();
if (NULL != hClipboardData) return;
{
LPTSTR pszText = static_cast<LPTSTR>(GlobalLock(hClipboardData));
if (NULL != pszText && isAllowed(pszText))
{
SendMessage(hEdit, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(pszText));
}
GlobalUnlock(hClipboardData);
}
CloseClipboard();
} }
LPTSTR pszText = static_cast<LPTSTR>(::GlobalLock(hClipboardData));
if (pszText)
{
if (isAllowed(pszText))
::SendMessage(hEdit, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(pszText));
::GlobalUnlock(hClipboardData);
}
::CloseClipboard();
} }
void StylerDlg::move2CtrlRight(HWND hwndDlg, int ctrlID, HWND handle2Move, int handle2MoveWidth, int handle2MoveHeight) void StylerDlg::move2CtrlRight(HWND hwndDlg, int ctrlID, HWND handle2Move, int handle2MoveWidth, int handle2MoveHeight)

View File

@ -230,18 +230,21 @@ intptr_t CALLBACK ClipboardHistoryPanel::run_dlgProc(UINT message, WPARAM wParam
::SendMessage(_hwndNextCbViewer, message, wParam, lParam); ::SendMessage(_hwndNextCbViewer, message, wParam, lParam);
return TRUE; return TRUE;
case WM_DRAWCLIPBOARD : case WM_DRAWCLIPBOARD:
{ {
ClipboardDataInfo clipboardData = getClipboadData(); if (_isTrackingClipboardOps)
if (clipboardData._data.size())
{ {
addToClipboadHistory(clipboardData); ClipboardDataInfo clipboardData = getClipboadData();
if (clipboardData._data.size())
{
addToClipboadHistory(clipboardData);
}
} }
if (_hwndNextCbViewer) if (_hwndNextCbViewer)
::SendMessage(_hwndNextCbViewer, message, wParam, lParam); ::SendMessage(_hwndNextCbViewer, message, wParam, lParam);
return TRUE; return TRUE;
} }
case WM_DESTROY: case WM_DESTROY:
::ChangeClipboardChain(_hSelf, _hwndNextCbViewer); ::ChangeClipboardChain(_hSelf, _hwndNextCbViewer);
break; break;

View File

@ -77,6 +77,12 @@ public:
void drawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); void drawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
bool trackClipboardOps(bool bTrack) {
bool bPreviousState = _isTrackingClipboardOps;
_isTrackingClipboardOps = bTrack;
return bPreviousState;
};
protected: protected:
virtual intptr_t CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam); virtual intptr_t CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);
@ -86,6 +92,6 @@ private:
HWND _hwndNextCbViewer = nullptr; HWND _hwndNextCbViewer = nullptr;
int _lbBgColor = -1; int _lbBgColor = -1;
int _lbFgColor= -1; int _lbFgColor= -1;
bool _isTrackingClipboardOps = true; // false when we do not want to track & show some Clipboard operations
}; };

View File

@ -879,31 +879,39 @@ void EditingSubDlg::changeLineHiliteMode(bool enableSlider)
bool hasOnlyNumSpaceInClipboard() bool hasOnlyNumSpaceInClipboard()
{ {
int clipFormat; unsigned int clipFormat = CF_UNICODETEXT;
clipFormat = CF_UNICODETEXT;
BOOL canPaste = ::IsClipboardFormatAvailable(clipFormat); if (!::IsClipboardFormatAvailable(clipFormat))
if (!canPaste) return false;
if (!::OpenClipboard(NULL))
return false; return false;
::OpenClipboard(NULL);
HANDLE clipboardData = ::GetClipboardData(clipFormat); HANDLE clipboardData = ::GetClipboardData(clipFormat);
if (!clipboardData) if (!clipboardData)
{
::CloseClipboard();
return false; return false;
}
::GlobalSize(clipboardData);
const wchar_t* clipboardDataPtr = (const wchar_t*)::GlobalLock(clipboardData); const wchar_t* clipboardDataPtr = (const wchar_t*)::GlobalLock(clipboardData);
if (!clipboardDataPtr) return false; if (!clipboardDataPtr)
{
::CloseClipboard();
return false;
}
wstring clipboardDataString = clipboardDataPtr; wstring clipboardDataString = clipboardDataPtr;
::GlobalUnlock(clipboardData); ::GlobalUnlock(clipboardData);
::CloseClipboard(); ::CloseClipboard();
for (wchar_t c: clipboardDataString) for (wchar_t c: clipboardDataString)
{ {
if (c != ' ' && (c < '0' || c > '9')) if (c != ' ' && (c < '0' || c > '9'))
return false; return false;
} }
return true; return true;
} }