1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 #pragma once 27 28 #include "jucer_DocumentEditorComponent.h" 29 30 //============================================================================== 31 class SourceCodeDocument : public OpenDocumentManager::Document 32 { 33 public: 34 SourceCodeDocument (Project*, const File&); 35 loadedOk()36 bool loadedOk() const override { return true; } isForFile(const File & file)37 bool isForFile (const File& file) const override { return getFile() == file; } isForNode(const ValueTree &)38 bool isForNode (const ValueTree&) const override { return false; } refersToProject(Project & p)39 bool refersToProject (Project& p) const override { return project == &p; } getProject()40 Project* getProject() const override { return project; } getName()41 String getName() const override { return getFile().getFileName(); } getType()42 String getType() const override { return getFile().getFileExtension() + " file"; } getFile()43 File getFile() const override { return modDetector.getFile(); } needsSaving()44 bool needsSaving() const override { return codeDoc != nullptr && codeDoc->hasChangedSinceSavePoint(); } hasFileBeenModifiedExternally()45 bool hasFileBeenModifiedExternally() override { return modDetector.hasBeenModified(); } fileHasBeenRenamed(const File & newFile)46 void fileHasBeenRenamed (const File& newFile) override { modDetector.fileHasBeenRenamed (newFile); } getState()47 String getState() const override { return lastState != nullptr ? lastState->toString() : String(); } restoreState(const String & state)48 void restoreState (const String& state) override { lastState.reset (new CodeEditorComponent::State (state)); } 49 getCounterpartFile()50 File getCounterpartFile() const override 51 { 52 auto file = getFile(); 53 54 if (file.hasFileExtension (sourceFileExtensions)) 55 { 56 static const char* extensions[] = { "h", "hpp", "hxx", "hh", nullptr }; 57 return findCounterpart (file, extensions); 58 } 59 60 if (file.hasFileExtension (headerFileExtensions)) 61 { 62 static const char* extensions[] = { "cpp", "mm", "cc", "cxx", "c", "m", nullptr }; 63 return findCounterpart (file, extensions); 64 } 65 66 return {}; 67 } 68 findCounterpart(const File & file,const char ** extensions)69 static File findCounterpart (const File& file, const char** extensions) 70 { 71 while (*extensions != nullptr) 72 { 73 auto f = file.withFileExtension (*extensions++); 74 75 if (f.existsAsFile()) 76 return f; 77 } 78 79 return {}; 80 } 81 82 void reloadFromFile() override; 83 bool save() override; 84 bool saveAs() override; 85 86 Component* createEditor() override; createViewer()87 Component* createViewer() override { return createEditor(); } 88 89 void updateLastState (CodeEditorComponent&); 90 void applyLastState (CodeEditorComponent&) const; 91 92 CodeDocument& getCodeDocument(); 93 94 //============================================================================== 95 struct Type : public OpenDocumentManager::DocumentType 96 { canOpenFileType97 bool canOpenFile (const File& file) override 98 { 99 if (file.hasFileExtension (sourceOrHeaderFileExtensions) 100 || file.hasFileExtension ("txt;inc;tcc;xml;plist;rtf;html;htm;php;py;rb;cs")) 101 return true; 102 103 MemoryBlock mb; 104 if (file.loadFileAsData (mb) 105 && seemsToBeText (static_cast<const char*> (mb.getData()), (int) mb.getSize()) 106 && ! file.hasFileExtension ("svg")) 107 return true; 108 109 return false; 110 } 111 seemsToBeTextType112 static bool seemsToBeText (const char* const chars, const int num) noexcept 113 { 114 for (int i = 0; i < num; ++i) 115 { 116 const char c = chars[i]; 117 if ((c < 32 && c != '\t' && c != '\r' && c != '\n') || chars[i] > 126) 118 return false; 119 } 120 121 return true; 122 } 123 openFileType124 Document* openFile (Project* p, const File& file) override { return new SourceCodeDocument (p, file); } 125 }; 126 127 protected: 128 FileModificationDetector modDetector; 129 std::unique_ptr<CodeDocument> codeDoc; 130 Project* project; 131 132 std::unique_ptr<CodeEditorComponent::State> lastState; 133 134 void reloadInternal(); 135 }; 136 137 class GenericCodeEditorComponent; 138 139 //============================================================================== 140 class SourceCodeEditor : public DocumentEditorComponent, 141 private ValueTree::Listener, 142 private CodeDocument::Listener 143 { 144 public: 145 SourceCodeEditor (OpenDocumentManager::Document*, CodeDocument&); 146 SourceCodeEditor (OpenDocumentManager::Document*, GenericCodeEditorComponent*); 147 ~SourceCodeEditor() override; 148 149 void scrollToKeepRangeOnScreen (Range<int> range); 150 void highlight (Range<int> range, bool cursorAtStart); 151 152 std::unique_ptr<GenericCodeEditorComponent> editor; 153 154 private: 155 void resized() override; 156 void lookAndFeelChanged() override; 157 158 void valueTreePropertyChanged (ValueTree&, const Identifier&) override; 159 void valueTreeChildAdded (ValueTree&, ValueTree&) override; 160 void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override; 161 void valueTreeChildOrderChanged (ValueTree&, int, int) override; 162 void valueTreeParentChanged (ValueTree&) override; 163 void valueTreeRedirected (ValueTree&) override; 164 165 void codeDocumentTextInserted (const String&, int) override; 166 void codeDocumentTextDeleted (int, int) override; 167 168 void setEditor (GenericCodeEditorComponent*); 169 void updateColourScheme(); 170 void checkSaveState(); 171 172 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SourceCodeEditor) 173 }; 174 175 176 //============================================================================== 177 class GenericCodeEditorComponent : public CodeEditorComponent 178 { 179 public: 180 GenericCodeEditorComponent (const File&, CodeDocument&, CodeTokeniser*); 181 ~GenericCodeEditorComponent() override; 182 183 void addPopupMenuItems (PopupMenu&, const MouseEvent*) override; 184 void performPopupMenuAction (int menuItemID) override; 185 186 void getAllCommands (Array<CommandID>&) override; 187 void getCommandInfo (CommandID, ApplicationCommandInfo&) override; 188 bool perform (const InvocationInfo&) override; 189 190 void showFindPanel(); 191 void hideFindPanel(); 192 void findSelection(); 193 void findNext (bool forwards, bool skipCurrentSelection); 194 void handleEscapeKey() override; 195 void editorViewportPositionChanged() override; 196 197 void resized() override; 198 getSearchString()199 static String getSearchString() { return getAppSettings().getGlobalProperties().getValue ("searchString"); } setSearchString(const String & s)200 static void setSearchString (const String& s) { getAppSettings().getGlobalProperties().setValue ("searchString", s); } isCaseSensitiveSearch()201 static bool isCaseSensitiveSearch() { return getAppSettings().getGlobalProperties().getBoolValue ("searchCaseSensitive"); } setCaseSensitiveSearch(bool b)202 static void setCaseSensitiveSearch (bool b) { getAppSettings().getGlobalProperties().setValue ("searchCaseSensitive", b); } 203 204 struct Listener 205 { ~ListenerListener206 virtual ~Listener() {} 207 virtual void codeEditorViewportMoved (CodeEditorComponent&) = 0; 208 }; 209 210 void addListener (Listener* listener); 211 void removeListener (Listener* listener); 212 213 private: 214 File file; 215 class FindPanel; 216 std::unique_ptr<FindPanel> findPanel; 217 ListenerList<Listener> listeners; 218 219 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericCodeEditorComponent) 220 }; 221 222 //============================================================================== 223 class CppCodeEditorComponent : public GenericCodeEditorComponent 224 { 225 public: 226 CppCodeEditorComponent (const File&, CodeDocument&); 227 ~CppCodeEditorComponent() override; 228 229 void addPopupMenuItems (PopupMenu&, const MouseEvent*) override; 230 void performPopupMenuAction (int menuItemID) override; 231 232 void handleReturnKey() override; 233 void insertTextAtCaret (const String& newText) override; 234 235 private: 236 void insertComponentClass(); 237 238 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CppCodeEditorComponent) 239 }; 240