Enhance multi-edit paste and Enter key type

Also disable auto-indent during multi-editing.

Ref: https://github.com/notepad-plus-plus/notepad-plus-plus/pull/14338#issuecomment-1809045648

Close #14355
pull/14356/head
Don Ho 1 year ago
parent 05f339b0cf
commit 1764758669

@ -356,6 +356,10 @@ LRESULT Notepad_plus::init(HWND hwnd)
_mainEditView.execute(SCI_SETMULTIPASTE, SC_MULTIPASTE_EACH);
_subEditView.execute(SCI_SETMULTIPASTE, SC_MULTIPASTE_EACH);
// Turn auto-completion into each multi-select on
_mainEditView.execute(SCI_AUTOCSETMULTI, SC_MULTIAUTOC_EACH);
_subEditView.execute(SCI_AUTOCSETMULTI, SC_MULTIAUTOC_EACH);
// allow user to start selecting as a stream block, then switch to a column block by adding Alt keypress
_mainEditView.execute(SCI_SETMOUSESELECTIONRECTANGULARSWITCH, true);
_subEditView.execute(SCI_SETMOUSESELECTIONRECTANGULARSWITCH, true);
@ -4218,8 +4222,8 @@ void Notepad_plus::updateStatusBar()
TCHAR strSel[64];
size_t numSelections = _pEditView->execute(SCI_GETSELECTIONS);
if (numSelections == 1)
size_t nbSelections = _pEditView->execute(SCI_GETSELECTIONS);
if (nbSelections == 1)
{
if (_pEditView->execute(SCI_GETSELECTIONEMPTY))
{
@ -4241,7 +4245,7 @@ void Notepad_plus::updateStatusBar()
bool sameCharCountOnEveryLine = true;
size_t maxLineCharCount = 0;
for (size_t sel = 0; sel < numSelections; ++sel)
for (size_t sel = 0; sel < nbSelections; ++sel)
{
size_t start = _pEditView->execute(SCI_GETSELECTIONNSTART, sel);
size_t end = _pEditView->execute(SCI_GETSELECTIONNEND, sel);
@ -4265,7 +4269,7 @@ void Notepad_plus::updateStatusBar()
}
wsprintf(strSel, TEXT("Sel : %sx%s %s %s"),
commafyInt(numSelections).c_str(), // lines (rows) in rectangular selection
commafyInt(nbSelections).c_str(), // lines (rows) in rectangular selection
commafyInt(maxLineCharCount).c_str(), // show maximum width for columns
sameCharCountOnEveryLine ? TEXT("=") : TEXT("->"),
commafyInt(rectSelCharsAndLines.first).c_str());
@ -4276,9 +4280,9 @@ void Notepad_plus::updateStatusBar()
const std::pair<size_t, size_t> multipleSelCharsAndLines = _pEditView->getSelectedCharsAndLinesCount(maxSelsToProcessLineCount);
wsprintf(strSel, TEXT("Sel %s : %s | %s"),
commafyInt(numSelections).c_str(),
commafyInt(nbSelections).c_str(),
commafyInt(multipleSelCharsAndLines.first).c_str(),
numSelections <= maxSelsToProcessLineCount ?
nbSelections <= maxSelsToProcessLineCount ?
commafyInt(multipleSelCharsAndLines.second).c_str() :
TEXT("...")); // show ellipsis for line count if too many selections are active
}

@ -443,10 +443,10 @@ void Notepad_plus::command(int id)
{
std::lock_guard<std::mutex> lock(command_mutex);
size_t numSelections = _pEditView->execute(SCI_GETSELECTIONS);
size_t nbSelections = _pEditView->execute(SCI_GETSELECTIONS);
Buffer* buf = getCurrentBuffer();
bool isRO = buf->isReadOnly();
if (numSelections > 1 && !isRO)
if (nbSelections > 1 && !isRO)
{
bool isPasteDone = _pEditView->pasteToMultiSelection();
if (isPasteDone)
@ -1783,10 +1783,10 @@ void Notepad_plus::command(int id)
bool forwards = id == IDM_EDIT_INS_TAB;
size_t selStartPos = _pEditView->execute(SCI_GETSELECTIONSTART);
size_t lineNumber = _pEditView->execute(SCI_LINEFROMPOSITION, selStartPos);
size_t numSelections = _pEditView->execute(SCI_GETSELECTIONS);
size_t nbSelections = _pEditView->execute(SCI_GETSELECTIONS);
size_t selEndPos = _pEditView->execute(SCI_GETSELECTIONEND);
size_t selEndLineNumber = _pEditView->execute(SCI_LINEFROMPOSITION, selEndPos);
if ((numSelections > 1) || (lineNumber != selEndLineNumber))
if ((nbSelections > 1) || (lineNumber != selEndLineNumber))
{
// multiple-selection or multi-line selection; use Scintilla SCI_TAB / SCI_BACKTAB behavior
_pEditView->execute(forwards ? SCI_TAB : SCI_BACKTAB);

@ -524,6 +524,7 @@ LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wPa
case VK_DOWN:
case VK_HOME:
case VK_END:
case VK_RETURN:
execute(SCI_SETSELECTIONMODE, SC_SEL_STREAM); // When it's rectangular selection and the arrow keys are pressed, we switch the mode for having multiple carets.
execute(SCI_SETSELECTIONMODE, SC_SEL_STREAM); // the 2nd call for removing the unwanted selection while moving carets.
@ -590,8 +591,8 @@ LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wPa
{
Buffer* buf = getCurrentBuffer();
bool isRO = buf->isReadOnly();
size_t numSelections = execute(SCI_GETSELECTIONS);
if (numSelections > 1 && !isRO)
size_t nbSelections = execute(SCI_GETSELECTIONS);
if (nbSelections > 1 && !isRO)
{
if (pasteToMultiSelection())
{
@ -603,7 +604,6 @@ LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wPa
}
}
}
break;
}
}
break;
@ -3092,43 +3092,101 @@ void ScintillaEditView::showIndentGuideLine(bool willBeShowed)
void ScintillaEditView::setLineIndent(size_t line, size_t indent) const
{
Sci_CharacterRangeFull crange = getSelection();
int64_t posBefore = execute(SCI_GETLINEINDENTPOSITION, line);
execute(SCI_SETLINEINDENTATION, line, indent);
int64_t posAfter = execute(SCI_GETLINEINDENTPOSITION, line);
long long posDifference = posAfter - posBefore;
if (posAfter > posBefore)
{
// Move selection on
if (crange.cpMin >= posBefore)
size_t nbSelections = execute(SCI_GETSELECTIONS);
if (nbSelections == 1)
{
Sci_CharacterRangeFull crange = getSelection();
int64_t posBefore = execute(SCI_GETLINEINDENTPOSITION, line);
execute(SCI_SETLINEINDENTATION, line, indent);
int64_t posAfter = execute(SCI_GETLINEINDENTPOSITION, line);
long long posDifference = posAfter - posBefore;
if (posAfter > posBefore)
{
crange.cpMin += static_cast<Sci_Position>(posDifference);
// Move selection on
if (crange.cpMin >= posBefore)
{
crange.cpMin += static_cast<Sci_Position>(posDifference);
}
if (crange.cpMax >= posBefore)
{
crange.cpMax += static_cast<Sci_Position>(posDifference);
}
}
if (crange.cpMax >= posBefore)
else if (posAfter < posBefore)
{
crange.cpMax += static_cast<Sci_Position>(posDifference);
// Move selection back
if (crange.cpMin >= posAfter)
{
if (crange.cpMin >= posBefore)
crange.cpMin += static_cast<Sci_Position>(posDifference);
else
crange.cpMin = static_cast<Sci_Position>(posAfter);
}
if (crange.cpMax >= posAfter)
{
if (crange.cpMax >= posBefore)
crange.cpMax += static_cast<Sci_Position>(posDifference);
else
crange.cpMax = static_cast<Sci_Position>(posAfter);
}
}
execute(SCI_SETSEL, crange.cpMin, crange.cpMax);
}
else if (posAfter < posBefore)
else
{
// Move selection back
if (crange.cpMin >= posAfter)
execute(SCI_BEGINUNDOACTION);
for (size_t i = 0; i < nbSelections; ++i)
{
if (crange.cpMin >= posBefore)
crange.cpMin += static_cast<Sci_Position>(posDifference);
else
crange.cpMin = static_cast<Sci_Position>(posAfter);
}
LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i);
LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i);
size_t l = execute(SCI_LINEFROMPOSITION, posStart);
int64_t posBefore = execute(SCI_GETLINEINDENTPOSITION, l);
execute(SCI_SETLINEINDENTATION, l, indent);
int64_t posAfter = execute(SCI_GETLINEINDENTPOSITION, l);
long long posDifference = posAfter - posBefore;
if (posAfter > posBefore)
{
// Move selection on
if (posStart >= posBefore)
{
posStart += static_cast<Sci_Position>(posDifference);
}
if (posEnd >= posBefore)
{
posEnd += static_cast<Sci_Position>(posDifference);
}
}
else if (posAfter < posBefore)
{
// Move selection back
if (posStart >= posAfter)
{
if (posStart >= posBefore)
posStart += static_cast<Sci_Position>(posDifference);
else
posStart = static_cast<Sci_Position>(posAfter);
}
if (crange.cpMax >= posAfter)
{
if (crange.cpMax >= posBefore)
crange.cpMax += static_cast<Sci_Position>(posDifference);
else
crange.cpMax = static_cast<Sci_Position>(posAfter);
if (posEnd >= posAfter)
{
if (posEnd >= posBefore)
posEnd += static_cast<Sci_Position>(posDifference);
else
posEnd = static_cast<Sci_Position>(posAfter);
}
}
execute(SCI_SETSELECTIONNSTART, i, posStart);
execute(SCI_SETSELECTIONNEND, i, posEnd);
}
execute(SCI_ENDUNDOACTION);
}
execute(SCI_SETSEL, crange.cpMin, crange.cpMax);
}
void ScintillaEditView::updateLineNumberWidth()
@ -3199,11 +3257,11 @@ void ScintillaEditView::setMultiSelections(const ColumnModeInfos & cmi)
// specify selectionNumber = -1 for the MAIN selection
pair<size_t, size_t> ScintillaEditView::getSelectionLinesRange(intptr_t selectionNumber /* = -1 */) const
{
size_t numSelections = execute(SCI_GETSELECTIONS);
size_t nbSelections = execute(SCI_GETSELECTIONS);
size_t start_pos, end_pos;
if ((selectionNumber < 0) || (static_cast<size_t>(selectionNumber) >= numSelections))
if ((selectionNumber < 0) || (static_cast<size_t>(selectionNumber) >= nbSelections))
{
start_pos = execute(SCI_GETSELECTIONSTART);
end_pos = execute(SCI_GETSELECTIONEND);
@ -4243,19 +4301,19 @@ pair<size_t, size_t> ScintillaEditView::getSelectedCharsAndLinesCount(long long
selectedCharsAndLines.first = getUnicodeSelectedLength();
size_t numSelections = execute(SCI_GETSELECTIONS);
size_t nbSelections = execute(SCI_GETSELECTIONS);
if (numSelections == 1)
if (nbSelections == 1)
{
pair<size_t, size_t> lineRange = getSelectionLinesRange();
selectedCharsAndLines.second = lineRange.second - lineRange.first + 1;
}
else if (execute(SCI_SELECTIONISRECTANGLE))
{
selectedCharsAndLines.second = numSelections;
selectedCharsAndLines.second = nbSelections;
}
else if ((maxSelectionsForLineCount == -1) || // -1 means process ALL of the selections
(numSelections <= static_cast<size_t>(maxSelectionsForLineCount)))
(nbSelections <= static_cast<size_t>(maxSelectionsForLineCount)))
{
// selections are obtained from Scintilla in the order user creates them,
// not in a lowest-to-highest position-based order;
@ -4264,7 +4322,7 @@ pair<size_t, size_t> ScintillaEditView::getSelectedCharsAndLinesCount(long long
// by selection into low-to-high line number order before processing them further
vector< pair <size_t, size_t> > v;
for (size_t s = 0; s < numSelections; ++s)
for (size_t s = 0; s < nbSelections; ++s)
{
v.push_back(getSelectionLinesRange(s));
}
@ -4287,9 +4345,9 @@ pair<size_t, size_t> ScintillaEditView::getSelectedCharsAndLinesCount(long long
size_t ScintillaEditView::getUnicodeSelectedLength() const
{
size_t length = 0;
size_t numSelections = execute(SCI_GETSELECTIONS);
size_t nbSelections = execute(SCI_GETSELECTIONS);
for (size_t s = 0; s < numSelections; ++s)
for (size_t s = 0; s < nbSelections; ++s)
{
size_t start = execute(SCI_GETSELECTIONNSTART, s);
size_t end = execute(SCI_GETSELECTIONNEND, s);
@ -4451,8 +4509,8 @@ void ScintillaEditView::removeAnyDuplicateLines()
bool ScintillaEditView::pasteToMultiSelection() const
{
size_t numSelections = execute(SCI_GETSELECTIONS);
if (numSelections <= 1)
size_t nbSelections = execute(SCI_GETSELECTIONS);
if (nbSelections <= 1)
return false;
// "MSDEVColumnSelect" is column format from Scintilla
@ -4468,19 +4526,50 @@ bool ScintillaEditView::pasteToMultiSelection() const
::GlobalUnlock(clipboardData);
::CloseClipboard();
vector<wstring> stringArray;
stringSplit(clipboardStr, getEOLString(), stringArray);
stringArray.erase(stringArray.cend() - 1); // remove the last empty string
vector<wstring> clipboardStrings;
stringSplit(clipboardStr, getEOLString(), clipboardStrings);
clipboardStrings.erase(clipboardStrings.cend() - 1); // remove the last empty string
size_t nbClipboardStr = clipboardStrings.size();
if (numSelections == stringArray.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 < numSelections; ++i)
for (size_t i = 0; i < nbClipboardStr; ++i)
{
LRESULT posStart = execute(SCI_GETSELECTIONNSTART, i);
LRESULT posEnd = execute(SCI_GETSELECTIONNEND, i);
replaceTarget(stringArray[i].c_str(), posStart, posEnd);
posStart += stringArray[i].length();
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);
}

Loading…
Cancel
Save