Fix Encoding "Convert to..." regression

Fix incorrect Clipboard handling. This commit fixes possibly #9426

Fix #15324, fix #15271, fix #3054, close #15346
This commit is contained in:
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))
{
::GlobalFree(hglbCopy);
::CloseClipboard();
return false;
}
if (!::EmptyClipboard())
@ -869,9 +868,8 @@ bool str2Clipboard(const generic_string &str2cpy, HWND hwnd)
}
// Lock the handle and copy the text to the buffer.
TCHAR *pStr = (TCHAR *)::GlobalLock(hglbCopy);
if (pStr == NULL)
if (!pStr)
{
::GlobalUnlock(hglbCopy);
::GlobalFree(hglbCopy);
::CloseClipboard();
return false;
@ -880,7 +878,7 @@ bool str2Clipboard(const generic_string &str2cpy, HWND hwnd)
::GlobalUnlock(hglbCopy);
// Place the handle on the clipboard.
unsigned int clipBoardFormat = CF_UNICODETEXT;
if (::SetClipboardData(clipBoardFormat, hglbCopy) == NULL)
if (!::SetClipboardData(clipBoardFormat, hglbCopy))
{
::GlobalFree(hglbCopy);
::CloseClipboard();

View File

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

View File

@ -427,51 +427,70 @@ void Notepad_plus::command(int id)
char *pBinText = new char[textLen + 1];
_pEditView->getSelectedText(pBinText, textLen + 1);
// Open the clipboard, and empty it.
if (!OpenClipboard(NULL))
// Open the clipboard and empty it.
if (!::OpenClipboard(NULL))
return;
EmptyClipboard();
if (!::EmptyClipboard())
{
::CloseClipboard();
return;
}
// Allocate a global memory object for the text.
HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (textLen + 1) * sizeof(unsigned char));
if (hglbCopy == NULL)
HGLOBAL hglbCopy = ::GlobalAlloc(GMEM_MOVEABLE, (textLen + 1) * sizeof(unsigned char));
if (!hglbCopy)
{
CloseClipboard();
::CloseClipboard();
return;
}
// 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));
lpucharCopy[textLen] = 0; // null character
delete[] pBinText;
GlobalUnlock(hglbCopy);
::GlobalUnlock(hglbCopy);
// 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.
HGLOBAL hglbLenCopy = GlobalAlloc(GMEM_MOVEABLE, sizeof(unsigned long));
if (hglbLenCopy == NULL)
HGLOBAL hglbLenCopy = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(unsigned long));
if (!hglbLenCopy)
{
CloseClipboard();
::CloseClipboard();
return;
}
// 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);
GlobalUnlock(hglbLenCopy);
::GlobalUnlock(hglbLenCopy);
// Place the handle on the clipboard.
UINT cf_nppTextLen = RegisterClipboardFormat(CF_NPPTEXTLEN);
SetClipboardData(cf_nppTextLen, hglbLenCopy);
UINT cf_nppTextLen = ::RegisterClipboardFormat(CF_NPPTEXTLEN);
::SetClipboardData(cf_nppTextLen, hglbLenCopy);
CloseClipboard();
::CloseClipboard();
if (id == IDM_EDIT_CUT_BINARY)
_pEditView->execute(SCI_REPLACESEL, 0, reinterpret_cast<LPARAM>(""));
@ -3122,44 +3141,95 @@ void Notepad_plus::command(int id)
if (idEncoding != -1)
{
// Save the current clipboard content
::OpenClipboard(_pPublicInterface->getHSelf());
HANDLE clipboardData = ::GetClipboardData(CF_TEXT);
LPVOID clipboardData2 = NULL;
if (clipboardData != NULL)
// try to save the current clipboard CF_TEXT content 1st
HGLOBAL hglbClipboardCopy = NULL;
if (::OpenClipboard(_pPublicInterface->getHSelf()))
{
int len = static_cast<int32_t>(::GlobalSize(clipboardData));
LPVOID clipboardDataPtr = ::GlobalLock(clipboardData);
HANDLE allocClipboardData = ::GlobalAlloc(GMEM_MOVEABLE, len);
clipboardData2 = ::GlobalLock(allocClipboardData);
::memcpy(clipboardData2, clipboardDataPtr, len);
::GlobalUnlock(clipboardData);
::GlobalUnlock(allocClipboardData);
HANDLE hClipboardData = ::GetClipboardData(CF_TEXT);
if (hClipboardData) // NULL if there is no previous CF_TEXT data in
{
LPVOID pClipboardData = ::GlobalLock(hClipboardData);
if (pClipboardData)
{
size_t clipboardDataSize = ::GlobalSize(pClipboardData);
hglbClipboardCopy = ::GlobalAlloc(GMEM_MOVEABLE, clipboardDataSize);
if (hglbClipboardCopy)
{
LPVOID pClipboardCopy = ::GlobalLock(hglbClipboardCopy);
if (pClipboardCopy)
{
::memcpy(pClipboardCopy, pClipboardData, clipboardDataSize);
::GlobalUnlock(hglbClipboardCopy);
}
else
{
::GlobalFree(hglbClipboardCopy);
hglbClipboardCopy = NULL;
}
}
::GlobalUnlock(hClipboardData);
}
}
::CloseClipboard();
}
_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
size_t docLen = _pEditView->getCurrentDocLen();
_pEditView->execute(SCI_COPYRANGE, 0, docLen);
_pEditView->execute(SCI_CLEARALL);
if (_pClipboardHistoryPanel)
_pClipboardHistoryPanel->trackClipboardOps(bPreviousCHPanelTrackingState); // restore
// Change to the proper buffer, save buffer status
::SendMessage(_pPublicInterface->getHSelf(), WM_COMMAND, idEncoding, 0);
// Paste the texte, restore buffer status
// Paste the text, restore buffer status
_pEditView->execute(SCI_PASTE);
_pEditView->restoreCurrentPosPreStep();
// Restore the previous clipboard data
::OpenClipboard(_pPublicInterface->getHSelf());
::EmptyClipboard();
::SetClipboardData(CF_TEXT, clipboardData2);
::CloseClipboard();
// Restore the previous Clipboard data if any
if (hglbClipboardCopy)
{
bool bAllOk = false;
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
_pEditView->execute(SCI_EMPTYUNDOBUFFER);

View File

@ -4591,68 +4591,77 @@ bool ScintillaEditView::pasteToMultiSelection() const
// "MSDEVColumnSelect" is column format from Scintilla
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);
::GlobalSize(clipboardData);
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;
}
}
::CloseClipboard();
return false;
}
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;
}

View File

@ -1734,22 +1734,26 @@ bool StringDlg::isAllowed([[maybe_unused]] const generic_string & txt)
void StringDlg::HandlePaste(HWND hEdit)
{
if (OpenClipboard(hEdit))
if (!::OpenClipboard(hEdit))
return;
HANDLE hClipboardData = ::GetClipboardData(CF_UNICODETEXT);
if (!hClipboardData)
{
HANDLE hClipboardData = GetClipboardData(CF_UNICODETEXT);
if (NULL != hClipboardData)
{
LPTSTR pszText = static_cast<LPTSTR>(GlobalLock(hClipboardData));
if (NULL != pszText && isAllowed(pszText))
{
SendMessage(hEdit, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(pszText));
}
GlobalUnlock(hClipboardData);
}
CloseClipboard();
::CloseClipboard();
return;
}
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)

View File

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

View File

@ -77,6 +77,12 @@ public:
void drawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
bool trackClipboardOps(bool bTrack) {
bool bPreviousState = _isTrackingClipboardOps;
_isTrackingClipboardOps = bTrack;
return bPreviousState;
};
protected:
virtual intptr_t CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);
@ -86,6 +92,6 @@ private:
HWND _hwndNextCbViewer = nullptr;
int _lbBgColor = -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()
{
int clipFormat;
clipFormat = CF_UNICODETEXT;
unsigned int clipFormat = CF_UNICODETEXT;
BOOL canPaste = ::IsClipboardFormatAvailable(clipFormat);
if (!canPaste)
if (!::IsClipboardFormatAvailable(clipFormat))
return false;
if (!::OpenClipboard(NULL))
return false;
::OpenClipboard(NULL);
HANDLE clipboardData = ::GetClipboardData(clipFormat);
if (!clipboardData)
{
::CloseClipboard();
return false;
}
::GlobalSize(clipboardData);
const wchar_t* clipboardDataPtr = (const wchar_t*)::GlobalLock(clipboardData);
if (!clipboardDataPtr) return false;
if (!clipboardDataPtr)
{
::CloseClipboard();
return false;
}
wstring clipboardDataString = clipboardDataPtr;
::GlobalUnlock(clipboardData);
::CloseClipboard();
for (wchar_t c: clipboardDataString)
{
if (c != ' ' && (c < '0' || c > '9'))
return false;
}
return true;
}