Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement rectangular selection editing #4326

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 43 additions & 6 deletions app/src/processing/app/syntax/InputHandler.java
Expand Up @@ -424,9 +424,7 @@ public void actionPerformed(ActionEvent evt) {
return;
}

if (textArea.getSelectionStart() != textArea.getSelectionStop()) {
textArea.setSelectedText("");
} else {
if (!selectionBackspaceDelete(textArea, false)) {
int caret = textArea.getCaretPosition();
if (caret == 0) {
textArea.getToolkit().beep();
Expand Down Expand Up @@ -498,9 +496,7 @@ public void actionPerformed(ActionEvent evt) {
return;
}

if (textArea.getSelectionStart() != textArea.getSelectionStop()) {
textArea.setSelectedText("");
} else {
if (!selectionBackspaceDelete(textArea, true)) {
int caret = textArea.getCaretPosition();
if (caret == textArea.getDocumentLength()) {
textArea.getToolkit().beep();
Expand Down Expand Up @@ -1183,4 +1179,45 @@ public static int findWordEnd(String line, int pos, String noWordSep) {
}
return wordEnd;
}

/**
* Called by the backspace and delete listeners to detect and handle
* selections, including rectangular ones.
* @return true iff this was a selection.
*/
static public boolean selectionBackspaceDelete(JEditTextArea textArea,
boolean delete) {
final int selectionStart = textArea.getSelectionStart();
final int selectionStop = textArea.getSelectionStop();
if (selectionStart == selectionStop) return false;

if (!textArea.rectSelect) {
textArea.setSelectedText("");
return true;
}

Element map = textArea.document.getDefaultRootElement();
Element startElement = map.getElement(textArea.getSelectionStartLine());
// Distance from start of line to the selection.
int start = selectionStart - startElement.getStartOffset();
int end = selectionStop -
map.getElement(textArea.getSelectionStopLine()).getStartOffset();

if (start != end) {
// Start and end of selection are not vertically aligned.
// Therefore there is a real rectangle selected, which we delete.
textArea.setSelectedText("");
} else if (delete && selectionStart != startElement.getEndOffset() - 1) {
// Widen the selection forwards one space, then clear it.
textArea.setSelectionStart(selectionStart + 1);
textArea.setSelectedText("");
} else if (!delete && start != 0) {
// Widen the selection backwards one space, then clear it.
textArea.setSelectionStart(selectionStart - 1);
textArea.setSelectedText("");
} else {
textArea.getToolkit().beep();
}
return true;
}
}
52 changes: 47 additions & 5 deletions app/src/processing/app/syntax/JEditTextArea.java
Expand Up @@ -1455,6 +1455,10 @@ public void setSelectedText(String selectedText) {
start = tmp;
}

// Text with new lines gets pasted in with one line of source text
// to each line of the destination.
final boolean putBlockIntoBlock = selectedText != null
&& selectedText.contains("\n");
int lastNewline = 0;
int currNewline = 0;

Expand All @@ -1466,28 +1470,45 @@ public void setSelectedText(String selectedText) {

document.remove(rectStart,Math.min(lineEnd - rectStart, end - start));

if (selectedText != null) {
if (selectedText == null) continue;

if (putBlockIntoBlock) {
currNewline = selectedText.indexOf('\n', lastNewline);
if (currNewline == -1) {
currNewline = selectedText.length();
}
document.insertString(rectStart, selectedText.substring(lastNewline, currNewline), null);
lastNewline = Math.min(selectedText.length(), currNewline + 1);
} else {
if (overwrite && end == start && selectedText.length() == 1) {
int newLineEnd = lineElement.getEndOffset() - 1;
document.remove(rectStart, Math.min(lineEnd - rectStart, selectedText.length()));
}
document.insertString(rectStart, selectedText, null);
}
}

if (selectedText != null &&
currNewline != selectedText.length()) {
if (putBlockIntoBlock && currNewline != selectedText.length()) {
int offset = map.getElement(selectionEndLine).getEndOffset() - 1;
document.insertString(offset, "\n", null);
document.insertString(offset + 1,selectedText.substring(currNewline + 1), null);
}

if (putBlockIntoBlock) {
// Since what we've pasted in is not guaranteed to be perfectly
// rectangular, the right-hand edge of it can't be selected.
setCaretPosition(selectionEnd);
} else {
select(selectionStart + selectedText.length(), selectionEnd);
}
} else {
document.remove(selectionStart, selectionEnd - selectionStart);
if (selectedText != null) {
document.insertString(selectionStart, selectedText,null);
}
setCaretPosition(selectionEnd);
}

} catch(BadLocationException bl) {
bl.printStackTrace();
throw new InternalError("Cannot replace selection");
Expand All @@ -1496,7 +1517,6 @@ public void setSelectedText(String selectedText) {
// No matter what happens... stops us from leaving document in a bad state
document.endCompoundEdit();
}
setCaretPosition(selectionEnd);
}


Expand Down Expand Up @@ -1564,6 +1584,7 @@ public final void setMagicCaretPosition(int magicCaret) {
public void overwriteSetSelectedText(String str)
{
// Don't overstrike if there is a selection
// Unless it's a rectangular one, which is handled in setSelectedText().
if(!overwrite || selectionStart != selectionEnd)
{
setSelectedText(str);
Expand Down Expand Up @@ -1989,7 +2010,28 @@ public void processKeyEvent(KeyEvent event) {
inputHandler.keyTyped(event);
break;
case KeyEvent.KEY_PRESSED:
inputHandler.keyPressed(event);
// Pressing enter with a rectangular selection handled here to avoid
// making it input handler-specific.
char c = event.getKeyChar();
if (!rectSelect || (c != '\n' && c != '\r') ||
(event.getModifiers() & InputEvent.META_MASK) != 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GKFX Quick question, why do you check META here?

inputHandler.keyPressed(event);
return;
}
document.beginCompoundEdit();
setSelectedText("");
Element map = document.getDefaultRootElement();
final int indent = selectionStart -
map.getElement(selectionStartLine).getStartOffset();
final int startLine = selectionStartLine;
final int endLine = 2*selectionEndLine - selectionStartLine;
for (int i = startLine; i <= endLine; i += 2) {
int caret = Math.min(map.getElement(i).getStartOffset() + indent,
map.getElement(i).getEndOffset());
select(caret, caret);
inputHandler.keyPressed(event);
}
document.endCompoundEdit();
break;
case KeyEvent.KEY_RELEASED:
inputHandler.keyReleased(event);
Expand Down