|
|
@@ -340,6 +340,7 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& doc, CodeTokeniser* cons |
|
|
|
columnsOnScreen (0),
|
|
|
|
scrollbarThickness (16),
|
|
|
|
columnToTryToMaintain (-1),
|
|
|
|
readOnly (false),
|
|
|
|
useSpacesForTabs (false),
|
|
|
|
showLineNumbers (false),
|
|
|
|
shouldFollowDocumentChanges (false),
|
|
|
@@ -435,6 +436,11 @@ void CodeEditorComponent::setLineNumbersShown (const bool shouldBeShown) |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CodeEditorComponent::setReadOnly (bool b) noexcept
|
|
|
|
{
|
|
|
|
readOnly = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void CodeEditorComponent::resized()
|
|
|
|
{
|
|
|
@@ -745,37 +751,43 @@ void CodeEditorComponent::insertTextAtCaret (const String& newText) |
|
|
|
|
|
|
|
void CodeEditorComponent::insertText (const String& newText)
|
|
|
|
{
|
|
|
|
document.deleteSection (selectionStart, selectionEnd);
|
|
|
|
if (! readOnly)
|
|
|
|
{
|
|
|
|
document.deleteSection (selectionStart, selectionEnd);
|
|
|
|
|
|
|
|
if (newText.isNotEmpty())
|
|
|
|
document.insertText (caretPos, newText);
|
|
|
|
if (newText.isNotEmpty())
|
|
|
|
document.insertText (caretPos, newText);
|
|
|
|
|
|
|
|
scrollToKeepCaretOnScreen();
|
|
|
|
scrollToKeepCaretOnScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CodeEditorComponent::insertTabAtCaret()
|
|
|
|
{
|
|
|
|
if (CharacterFunctions::isWhitespace (caretPos.getCharacter())
|
|
|
|
&& caretPos.getLineNumber() == caretPos.movedBy (1).getLineNumber())
|
|
|
|
if (! readOnly)
|
|
|
|
{
|
|
|
|
moveCaretTo (document.findWordBreakAfter (caretPos), false);
|
|
|
|
}
|
|
|
|
if (CharacterFunctions::isWhitespace (caretPos.getCharacter())
|
|
|
|
&& caretPos.getLineNumber() == caretPos.movedBy (1).getLineNumber())
|
|
|
|
{
|
|
|
|
moveCaretTo (document.findWordBreakAfter (caretPos), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (useSpacesForTabs)
|
|
|
|
{
|
|
|
|
const int caretCol = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine());
|
|
|
|
const int spacesNeeded = spacesPerTab - (caretCol % spacesPerTab);
|
|
|
|
insertTextAtCaret (String::repeatedString (" ", spacesNeeded));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
insertTextAtCaret ("\t");
|
|
|
|
if (useSpacesForTabs)
|
|
|
|
{
|
|
|
|
const int caretCol = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine());
|
|
|
|
const int spacesNeeded = spacesPerTab - (caretCol % spacesPerTab);
|
|
|
|
insertTextAtCaret (String::repeatedString (" ", spacesNeeded));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
insertTextAtCaret ("\t");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CodeEditorComponent::deleteWhitespaceBackwardsToTabStop()
|
|
|
|
{
|
|
|
|
if (getHighlightedRegion().isEmpty())
|
|
|
|
if (getHighlightedRegion().isEmpty() && ! readOnly)
|
|
|
|
{
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
@@ -804,43 +816,46 @@ void CodeEditorComponent::unindentSelection() { indentSelectedLines (-spacesPe |
|
|
|
|
|
|
|
void CodeEditorComponent::indentSelectedLines (const int spacesToAdd)
|
|
|
|
{
|
|
|
|
newTransaction();
|
|
|
|
|
|
|
|
CodeDocument::Position oldSelectionStart (selectionStart), oldSelectionEnd (selectionEnd), oldCaret (caretPos);
|
|
|
|
oldSelectionStart.setPositionMaintained (true);
|
|
|
|
oldSelectionEnd.setPositionMaintained (true);
|
|
|
|
oldCaret.setPositionMaintained (true);
|
|
|
|
if (! readOnly)
|
|
|
|
{
|
|
|
|
newTransaction();
|
|
|
|
|
|
|
|
const int lineStart = selectionStart.getLineNumber();
|
|
|
|
int lineEnd = selectionEnd.getLineNumber();
|
|
|
|
CodeDocument::Position oldSelectionStart (selectionStart), oldSelectionEnd (selectionEnd), oldCaret (caretPos);
|
|
|
|
oldSelectionStart.setPositionMaintained (true);
|
|
|
|
oldSelectionEnd.setPositionMaintained (true);
|
|
|
|
oldCaret.setPositionMaintained (true);
|
|
|
|
|
|
|
|
if (lineEnd > lineStart && selectionEnd.getIndexInLine() == 0)
|
|
|
|
--lineEnd;
|
|
|
|
const int lineStart = selectionStart.getLineNumber();
|
|
|
|
int lineEnd = selectionEnd.getLineNumber();
|
|
|
|
|
|
|
|
for (int line = lineStart; line <= lineEnd; ++line)
|
|
|
|
{
|
|
|
|
const String lineText (document.getLine (line));
|
|
|
|
const int nonWhitespaceStart = CodeEditorHelpers::findFirstNonWhitespaceChar (lineText);
|
|
|
|
if (lineEnd > lineStart && selectionEnd.getIndexInLine() == 0)
|
|
|
|
--lineEnd;
|
|
|
|
|
|
|
|
if (nonWhitespaceStart > 0 || lineText.trimStart().isNotEmpty())
|
|
|
|
for (int line = lineStart; line <= lineEnd; ++line)
|
|
|
|
{
|
|
|
|
const CodeDocument::Position wsStart (document, line, 0);
|
|
|
|
const CodeDocument::Position wsEnd (document, line, nonWhitespaceStart);
|
|
|
|
|
|
|
|
const int numLeadingSpaces = indexToColumn (line, wsEnd.getIndexInLine());
|
|
|
|
const int newNumLeadingSpaces = jmax (0, numLeadingSpaces + spacesToAdd);
|
|
|
|
const String lineText (document.getLine (line));
|
|
|
|
const int nonWhitespaceStart = CodeEditorHelpers::findFirstNonWhitespaceChar (lineText);
|
|
|
|
|
|
|
|
if (newNumLeadingSpaces != numLeadingSpaces)
|
|
|
|
if (nonWhitespaceStart > 0 || lineText.trimStart().isNotEmpty())
|
|
|
|
{
|
|
|
|
document.deleteSection (wsStart, wsEnd);
|
|
|
|
document.insertText (wsStart, getTabString (newNumLeadingSpaces));
|
|
|
|
const CodeDocument::Position wsStart (document, line, 0);
|
|
|
|
const CodeDocument::Position wsEnd (document, line, nonWhitespaceStart);
|
|
|
|
|
|
|
|
const int numLeadingSpaces = indexToColumn (line, wsEnd.getIndexInLine());
|
|
|
|
const int newNumLeadingSpaces = jmax (0, numLeadingSpaces + spacesToAdd);
|
|
|
|
|
|
|
|
if (newNumLeadingSpaces != numLeadingSpaces)
|
|
|
|
{
|
|
|
|
document.deleteSection (wsStart, wsEnd);
|
|
|
|
document.insertText (wsStart, getTabString (newNumLeadingSpaces));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
selectionStart = oldSelectionStart;
|
|
|
|
selectionEnd = oldSelectionEnd;
|
|
|
|
caretPos = oldCaret;
|
|
|
|
selectionStart = oldSelectionStart;
|
|
|
|
selectionEnd = oldSelectionEnd;
|
|
|
|
caretPos = oldCaret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CodeEditorComponent::cut()
|
|
|
@@ -1116,6 +1131,9 @@ void CodeEditorComponent::selectRegion (const CodeDocument::Position& start, |
|
|
|
//==============================================================================
|
|
|
|
bool CodeEditorComponent::undo()
|
|
|
|
{
|
|
|
|
if (readOnly)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ScopedValueSetter<bool> svs (shouldFollowDocumentChanges, true, false);
|
|
|
|
document.undo();
|
|
|
|
scrollToKeepCaretOnScreen();
|
|
|
@@ -1124,6 +1142,9 @@ bool CodeEditorComponent::undo() |
|
|
|
|
|
|
|
bool CodeEditorComponent::redo()
|
|
|
|
{
|
|
|
|
if (readOnly)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ScopedValueSetter<bool> svs (shouldFollowDocumentChanges, true, false);
|
|
|
|
document.redo();
|
|
|
|
scrollToKeepCaretOnScreen();
|
|
|
@@ -1169,6 +1190,9 @@ bool CodeEditorComponent::keyPressed (const KeyPress& key) |
|
|
|
{
|
|
|
|
if (! TextEditorKeyMapper<CodeEditorComponent>::invokeKeyFunction (*this, key))
|
|
|
|
{
|
|
|
|
if (readOnly)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (key == KeyPress::tabKey || key.getTextCharacter() == '\t') handleTabKey();
|
|
|
|
else if (key == KeyPress::returnKey) handleReturnKey();
|
|
|
|
else if (key == KeyPress::escapeKey) handleEscapeKey();
|
|
|
@@ -1224,7 +1248,7 @@ void CodeEditorComponent::getCommandInfo (const CommandID commandID, Application |
|
|
|
{
|
|
|
|
case StandardApplicationCommandIDs::cut:
|
|
|
|
result.setInfo (TRANS ("Cut"), TRANS ("Copies the currently selected text to the clipboard and deletes it."), "Editing", 0);
|
|
|
|
result.setActive (anythingSelected);
|
|
|
|
result.setActive (anythingSelected && ! readOnly);
|
|
|
|
result.defaultKeypresses.add (KeyPress ('x', ModifierKeys::commandModifier, 0));
|
|
|
|
break;
|
|
|
|
|
|
|
@@ -1236,12 +1260,13 @@ void CodeEditorComponent::getCommandInfo (const CommandID commandID, Application |
|
|
|
|
|
|
|
case StandardApplicationCommandIDs::paste:
|
|
|
|
result.setInfo (TRANS ("Paste"), TRANS ("Inserts text from the clipboard."), "Editing", 0);
|
|
|
|
result.setActive (! readOnly);
|
|
|
|
result.defaultKeypresses.add (KeyPress ('v', ModifierKeys::commandModifier, 0));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case StandardApplicationCommandIDs::del:
|
|
|
|
result.setInfo (TRANS ("Delete"), TRANS ("Deletes any selected text."), "Editing", 0);
|
|
|
|
result.setActive (anythingSelected);
|
|
|
|
result.setActive (anythingSelected && ! readOnly);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case StandardApplicationCommandIDs::selectAll:
|
|
|
@@ -1252,13 +1277,13 @@ void CodeEditorComponent::getCommandInfo (const CommandID commandID, Application |
|
|
|
case StandardApplicationCommandIDs::undo:
|
|
|
|
result.setInfo (TRANS ("Undo"), TRANS ("Undo"), "Editing", 0);
|
|
|
|
result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier, 0));
|
|
|
|
result.setActive (document.getUndoManager().canUndo());
|
|
|
|
result.setActive (document.getUndoManager().canUndo() && ! readOnly);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case StandardApplicationCommandIDs::redo:
|
|
|
|
result.setInfo (TRANS ("Redo"), TRANS ("Redo"), "Editing", 0);
|
|
|
|
result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
|
|
result.setActive (document.getUndoManager().canRedo());
|
|
|
|
result.setActive (document.getUndoManager().canRedo() && ! readOnly);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|