From 3c3e7cdadd5de424bc5d1568799ee33c78e07ee2 Mon Sep 17 00:00:00 2001 From: xomx Date: Mon, 24 Jun 2024 00:49:50 +0200 Subject: [PATCH] Fix Encoding "Convert to..." regression Fix incorrect Clipboard handling. This commit fixes possibly #9426 Fix #15324, fix #15271, fix #3054, close #15346 --- PowerEditor/src/MISC/Common/Common.cpp | 6 +- PowerEditor/src/Notepad_plus.cpp | 24 ++- PowerEditor/src/NppCommands.cpp | 146 +++++++++++++----- .../ScintillaComponent/ScintillaEditView.cpp | 131 ++++++++-------- .../ScintillaComponent/UserDefineDialog.cpp | 32 ++-- .../clipboardHistoryPanel.cpp | 13 +- .../ClipboardHistory/clipboardHistoryPanel.h | 8 +- .../WinControls/Preference/preferenceDlg.cpp | 22 ++- 8 files changed, 245 insertions(+), 137 deletions(-) diff --git a/PowerEditor/src/MISC/Common/Common.cpp b/PowerEditor/src/MISC/Common/Common.cpp index 1a5066350..134ada74a 100644 --- a/PowerEditor/src/MISC/Common/Common.cpp +++ b/PowerEditor/src/MISC/Common/Common.cpp @@ -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(); diff --git a/PowerEditor/src/Notepad_plus.cpp b/PowerEditor/src/Notepad_plus.cpp index 31a0081a8..d327bc41e 100644 --- a/PowerEditor/src/Notepad_plus.cpp +++ b/PowerEditor/src/Notepad_plus.cpp @@ -2870,19 +2870,29 @@ void Notepad_plus::pasteToMarkedLines() { std::lock_guard 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; diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index f5e00d7ee..03c24ff39 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -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(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("")); @@ -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(::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); diff --git a/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp b/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp index bc440a42b..e945e7d80 100644 --- a/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp +++ b/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp @@ -4591,68 +4591,77 @@ bool ScintillaEditView::pasteToMultiSelection() const // "MSDEVColumnSelect" is column format from Scintilla CLIPFORMAT cfColumnSelect = static_cast(::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 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(clipboardDataPtr); + ::GlobalUnlock(clipboardData); + ::CloseClipboard(); + + vector 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; } diff --git a/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp b/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp index c37a7082f..3370139b5 100644 --- a/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp +++ b/PowerEditor/src/ScintillaComponent/UserDefineDialog.cpp @@ -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(GlobalLock(hClipboardData)); - if (NULL != pszText && isAllowed(pszText)) - { - SendMessage(hEdit, EM_REPLACESEL, TRUE, reinterpret_cast(pszText)); - } - - GlobalUnlock(hClipboardData); - } - - CloseClipboard(); + ::CloseClipboard(); + return; } + + LPTSTR pszText = static_cast(::GlobalLock(hClipboardData)); + if (pszText) + { + if (isAllowed(pszText)) + ::SendMessage(hEdit, EM_REPLACESEL, TRUE, reinterpret_cast(pszText)); + + ::GlobalUnlock(hClipboardData); + } + + ::CloseClipboard(); } void StylerDlg::move2CtrlRight(HWND hwndDlg, int ctrlID, HWND handle2Move, int handle2MoveWidth, int handle2MoveHeight) diff --git a/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.cpp b/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.cpp index 2b8238371..4a189c8e9 100644 --- a/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.cpp +++ b/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.cpp @@ -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; diff --git a/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.h b/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.h index 9e02f9fe2..2601523af 100644 --- a/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.h +++ b/PowerEditor/src/WinControls/ClipboardHistory/clipboardHistoryPanel.h @@ -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 }; diff --git a/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp b/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp index cda0b004b..077cecc7d 100644 --- a/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp +++ b/PowerEditor/src/WinControls/Preference/preferenceDlg.cpp @@ -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; }