Skip to content

Commit

Permalink
Enhance multi-edit paste and Enter key type
Browse files Browse the repository at this point in the history
Also disable auto-indent during multi-editing.

Ref: notepad-plus-plus#14338 (comment)

Close notepad-plus-plus#14355
  • Loading branch information
donho committed Nov 15, 2023
1 parent 05f339b commit 1764758
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 58 deletions.
16 changes: 10 additions & 6 deletions PowerEditor/src/Notepad_plus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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))
{
Expand All @@ -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);
Expand All @@ -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());
Expand All @@ -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
}
Expand Down
8 changes: 4 additions & 4 deletions PowerEditor/src/NppCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
185 changes: 137 additions & 48 deletions PowerEditor/src/ScintillaComponent/ScintillaEditView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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())
{
Expand All @@ -603,7 +604,6 @@ LRESULT ScintillaEditView::scintillaNew_Proc(HWND hwnd, UINT Message, WPARAM wPa
}
}
}
break;
}
}
break;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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));
}
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
Expand Down

0 comments on commit 1764758

Please sign in to comment.