From f89c2ca05d9dda84ebdd2ac2589e277c2593dd3b Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 3 Dec 2013 19:09:16 +0000 Subject: [PATCH] Added read-only support for the CodeEditorComponent. --- .../code_editor/juce_CodeEditorComponent.cpp | 123 +++++++++++------- .../code_editor/juce_CodeEditorComponent.h | 8 +- 2 files changed, 81 insertions(+), 50 deletions(-) diff --git a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp index 066904d920..a67c42d64b 100644 --- a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -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 svs (shouldFollowDocumentChanges, true, false); document.undo(); scrollToKeepCaretOnScreen(); @@ -1124,6 +1142,9 @@ bool CodeEditorComponent::undo() bool CodeEditorComponent::redo() { + if (readOnly) + return false; + ScopedValueSetter svs (shouldFollowDocumentChanges, true, false); document.redo(); scrollToKeepCaretOnScreen(); @@ -1169,6 +1190,9 @@ bool CodeEditorComponent::keyPressed (const KeyPress& key) { if (! TextEditorKeyMapper::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: diff --git a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h index 3918eda8fd..bb3fb166d4 100644 --- a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h +++ b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h @@ -204,6 +204,12 @@ public: /** Returns the font that the editor is using. */ const Font& getFont() const noexcept { return font; } + /** Makes the editor read-only. */ + void setReadOnly (bool shouldBeReadOnly) noexcept; + + /** Returns true if the editor is set to be read-only. */ + bool isReadOnly() const noexcept { return readOnly; } + //============================================================================== struct JUCE_API ColourScheme { @@ -352,7 +358,7 @@ private: float charWidth; int lineHeight, linesOnScreen, columnsOnScreen; int scrollbarThickness, columnToTryToMaintain; - bool useSpacesForTabs, showLineNumbers, shouldFollowDocumentChanges; + bool readOnly, useSpacesForTabs, showLineNumbers, shouldFollowDocumentChanges; double xOffset; CodeDocument::Position caretPos, selectionStart, selectionEnd;