diff --git a/PowerEditor/src/NppBigSwitch.cpp b/PowerEditor/src/NppBigSwitch.cpp index d7cd42bf7cb..08a8c03f68d 100644 --- a/PowerEditor/src/NppBigSwitch.cpp +++ b/PowerEditor/src/NppBigSwitch.cpp @@ -2276,6 +2276,13 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa return TRUE; } + case NPPM_INTERNAL_CHECKUNDOREDOSTATE: + { + checkClipboard(); + checkUndoState(); + return TRUE; + } + case WM_QUERYENDSESSION: { // app should return TRUE or FALSE immediately upon receiving this message, diff --git a/PowerEditor/src/NppCommands.cpp b/PowerEditor/src/NppCommands.cpp index 28b7d80158d..688eb44f319 100644 --- a/PowerEditor/src/NppCommands.cpp +++ b/PowerEditor/src/NppCommands.cpp @@ -429,8 +429,8 @@ void Notepad_plus::command(int id) GlobalUnlock(hglbLenCopy); // Place the handle on the clipboard. - UINT f = RegisterClipboardFormat(CF_NPPTEXTLEN); - SetClipboardData(f, hglbLenCopy); + UINT cf_nppTextLen = RegisterClipboardFormat(CF_NPPTEXTLEN); + SetClipboardData(cf_nppTextLen, hglbLenCopy); CloseClipboard(); @@ -442,6 +442,17 @@ void Notepad_plus::command(int id) case IDM_EDIT_PASTE: { std::lock_guard lock(command_mutex); + + size_t numSelections = _pEditView->execute(SCI_GETSELECTIONS); + Buffer* buf = getCurrentBuffer(); + bool isRO = buf->isReadOnly(); + if (numSelections > 1 && !isRO) + { + bool isPasteDone = _pEditView->pasteToMultiSelection(); + if (isPasteDone) + return; + } + intptr_t eolMode = _pEditView->execute(SCI_GETEOLMODE); _pEditView->execute(SCI_PASTE); _pEditView->execute(SCI_CONVERTEOLS, eolMode); diff --git a/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp b/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp index 08b99aa606f..dae26dade5c 100644 --- a/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp +++ b/PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp @@ -496,6 +496,16 @@ LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wPa ::SendMessage(_hParent, WM_NOTIFY, LINKTRIGGERED, reinterpret_cast(¬ification)); } + else if (wParam == 'V') + { + if (_isMultiPasteActive) + { + Buffer* buf = getCurrentBuffer(); + buf->setUserReadOnly(false); + _isMultiPasteActive = false; + ::SendMessage(_hParent, NPPM_INTERNAL_CHECKUNDOREDOSTATE, 0, 0); + } + } break; } @@ -570,6 +580,30 @@ LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wPa } } break; + + case 'V': + { + SHORT ctrl = GetKeyState(VK_CONTROL); + SHORT alt = GetKeyState(VK_MENU); + SHORT shift = GetKeyState(VK_SHIFT); + if ((ctrl & 0x8000) && !(alt & 0x8000) && !(shift & 0x8000)) + { + Buffer* buf = getCurrentBuffer(); + bool isRO = buf->isReadOnly(); + size_t numSelections = execute(SCI_GETSELECTIONS); + if (numSelections > 1 && !isRO) + { + if (pasteToMultiSelection()) + { + // Hack for preventing the char "SYN" (0x16) from being adding into edit zone + buf->setUserReadOnly(true); + + _isMultiPasteActive = true; // It will be set false with WM_KEYUP message + } + } + } + } + break; } } break; @@ -4106,7 +4140,7 @@ void ScintillaEditView::changeTextDirection(bool isRTL) } } -generic_string ScintillaEditView::getEOLString() +generic_string ScintillaEditView::getEOLString() const { intptr_t eol_mode = execute(SCI_GETEOLMODE); if (eol_mode == SC_EOL_CRLF) @@ -4394,3 +4428,46 @@ void ScintillaEditView::removeAnyDuplicateLines() } } } + +bool ScintillaEditView::pasteToMultiSelection() const +{ + size_t numSelections = execute(SCI_GETSELECTIONS); + if (numSelections <= 1) + return false; + + // "MSDEVColumnSelect" is column format from Scintilla + CLIPFORMAT cfColumnSelect = static_cast(::RegisterClipboardFormat(TEXT("MSDEVColumnSelect"))); + if (IsClipboardFormatAvailable(cfColumnSelect) && OpenClipboard(NULL)) + { + HANDLE clipboardData = ::GetClipboardData(CF_UNICODETEXT); + ::GlobalSize(clipboardData); + LPVOID clipboardDataPtr = ::GlobalLock(clipboardData); + if (clipboardDataPtr) + { + wstring clipboardStr = (const TCHAR*)clipboardDataPtr; + ::GlobalUnlock(clipboardData); + ::CloseClipboard(); + + vector stringArray; + stringSplit(clipboardStr, getEOLString(), stringArray); + stringArray.erase(stringArray.cend() - 1); // remove the last empty string + + if (numSelections == stringArray.size()) + { + execute(SCI_BEGINUNDOACTION); + for (size_t i = 0; i < numSelections; ++i) + { + LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i); + LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i); + replaceTarget(stringArray[i].c_str(), posStart, posEnd); + posStart += stringArray[i].length(); + execute(SCI_SETSELECTIONNSTART, i, posStart); + execute(SCI_SETSELECTIONNEND, i, posStart); + } + execute(SCI_ENDUNDOACTION); + return true; + } + } + } + return false; +} diff --git a/PowerEditor/src/ScintillaComponent/ScintillaEditView.h b/PowerEditor/src/ScintillaComponent/ScintillaEditView.h index 9e9671ceb0b..c1ba9a31be9 100644 --- a/PowerEditor/src/ScintillaComponent/ScintillaEditView.h +++ b/PowerEditor/src/ScintillaComponent/ScintillaEditView.h @@ -768,7 +768,7 @@ friend class Finder; (_codepage == CP_JAPANESE) || (_codepage == CP_KOREAN)); }; void scrollPosToCenter(size_t pos); - generic_string getEOLString(); + generic_string getEOLString() const; void setBorderEdge(bool doWithBorderEdge); void sortLines(size_t fromLine, size_t toLine, ISorter *pSort); void changeTextDirection(bool isRTL); @@ -777,6 +777,7 @@ friend class Finder; void markedTextToClipboard(int indiStyle, bool doAll = false); void removeAnyDuplicateLines(); bool expandWordSelection(); + bool pasteToMultiSelection() const; protected: static bool _SciInit; @@ -811,8 +812,8 @@ friend class Finder; BufferStyleMap _hotspotStyles; intptr_t _beginSelectPosition = -1; - static std::string _defaultCharList; + bool _isMultiPasteActive = false; //Lexers and Styling void restyleBuffer(); diff --git a/PowerEditor/src/resource.h b/PowerEditor/src/resource.h index 54dc54bbf87..b6fbb5c6997 100644 --- a/PowerEditor/src/resource.h +++ b/PowerEditor/src/resource.h @@ -655,6 +655,7 @@ #define NPPM_INTERNAL_NPCLAUNCHSTYLECONF (NOTEPADPLUS_USER_INTERNAL + 74) #define NPPM_INTERNAL_CLOSEDOC (NOTEPADPLUS_USER_INTERNAL + 75) #define NPPM_INTERNAL_EXTERNALLEXERBUFFER (NOTEPADPLUS_USER_INTERNAL + 76) + #define NPPM_INTERNAL_CHECKUNDOREDOSTATE (NOTEPADPLUS_USER_INTERNAL + 77) // See Notepad_plus_msgs.h //#define NOTEPADPLUS_USER (WM_USER + 1000) diff --git a/scintilla/src/Editor.cxx b/scintilla/src/Editor.cxx index a9cd01062c4..12f9862c669 100644 --- a/scintilla/src/Editor.cxx +++ b/scintilla/src/Editor.cxx @@ -4337,7 +4337,7 @@ void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) { std::sort(rangesInOrder.begin(), rangesInOrder.end()); for (const SelectionRange ¤t : rangesInOrder) { text.append(RangeText(current.Start().Position(), current.End().Position())); - if (sel.selType == Selection::SelTypes::rectangle) { + if (rangesInOrder.size() > 1) { if (pdoc->eolMode != EndOfLine::Lf) text.push_back('\r'); if (pdoc->eolMode != EndOfLine::Cr) @@ -4345,7 +4345,7 @@ void Editor::CopySelectionRange(SelectionText *ss, bool allowLineCopy) { } } ss->Copy(text, pdoc->dbcsCodePage, - vs.styles[StyleDefault].characterSet, sel.IsRectangular(), sel.selType == Selection::SelTypes::lines); + vs.styles[StyleDefault].characterSet, rangesInOrder.size() > 1, sel.selType == Selection::SelTypes::lines); } }