| @@ -304,15 +304,79 @@ void CodeGenerator::applyToCode (String& code, const File& targetFile, | |||
| //============================================================================== | |||
| CodeGenerator::CustomisedCodeSnippets::CustomisedCodeSnippets() | |||
| CodeGenerator::CustomCodeList::Iterator::Iterator (const String& documentText, CustomCodeList& customCode_) | |||
| : customCode (customCode_), i (0), codeDocument (0) | |||
| { | |||
| lines.addLines (documentText); | |||
| } | |||
| CodeGenerator::CustomisedCodeSnippets::~CustomisedCodeSnippets() | |||
| CodeGenerator::CustomCodeList::Iterator::~Iterator() | |||
| { | |||
| } | |||
| void CodeGenerator::CustomisedCodeSnippets::reloadFrom (const String& fileContent) | |||
| bool CodeGenerator::CustomCodeList::Iterator::next() | |||
| { | |||
| textBefore = String::empty; | |||
| textAfter = String::empty; | |||
| while (i < lines.size()) | |||
| { | |||
| textBefore += lines[i] + "\n"; | |||
| if (lines[i].trimStart().startsWith ("//[")) | |||
| { | |||
| String tag (lines[i].trimStart().substring (3)); | |||
| tag = tag.upToFirstOccurrenceOf ("]", false, false).trim(); | |||
| if (! (tag.isEmpty() || tag.startsWithChar ('/'))) | |||
| { | |||
| const int endLine = indexOfLineStartingWith (lines, "//[/" + tag + "]", i + 1); | |||
| if (endLine > i) | |||
| { | |||
| sectionName = tag; | |||
| codeDocument = customCode.getDocumentFor (tag, true); | |||
| i = endLine; | |||
| bool isLastTag = true; | |||
| for (int j = i + 1; j < lines.size(); ++j) | |||
| { | |||
| if (lines[j].trimStart().startsWith ("//[")) | |||
| { | |||
| isLastTag = false; | |||
| break; | |||
| } | |||
| } | |||
| if (isLastTag) | |||
| { | |||
| textAfter = lines.joinIntoString (newLine, i, lines.size() - i); | |||
| i = lines.size(); | |||
| } | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| ++i; | |||
| } | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| CodeGenerator::CustomCodeList::CustomCodeList() | |||
| { | |||
| } | |||
| CodeGenerator::CustomCodeList::~CustomCodeList() | |||
| { | |||
| } | |||
| void CodeGenerator::CustomCodeList::reloadFrom (const String& fileContent) | |||
| { | |||
| sectionNames.clear(); | |||
| sectionContent.clear(); | |||
| @@ -339,19 +403,23 @@ void CodeGenerator::CustomisedCodeSnippets::reloadFrom (const String& fileConten | |||
| sectionNames.add (tag); | |||
| CodeDocument* doc = new CodeDocument(); | |||
| CodeDocumentRef::Ptr doc (new CodeDocumentRef (new CodeDocument())); | |||
| sectionContent.add (doc); | |||
| doc->replaceAllContent (content); | |||
| doc->getDocument().replaceAllContent (content); | |||
| doc->getDocument().clearUndoHistory(); | |||
| doc->getDocument().setSavePoint(); | |||
| i = endLine; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| sendSynchronousChangeMessage (this); | |||
| } | |||
| void CodeGenerator::CustomisedCodeSnippets::applyTo (String& fileContent) const | |||
| void CodeGenerator::CustomCodeList::applyTo (String& fileContent) const | |||
| { | |||
| StringArray lines; | |||
| lines.addLines (fileContent); | |||
| @@ -400,12 +468,31 @@ void CodeGenerator::CustomisedCodeSnippets::applyTo (String& fileContent) const | |||
| fileContent = lines.joinIntoString (newLine); | |||
| } | |||
| bool CodeGenerator::CustomisedCodeSnippets::areAnySnippetsUnsaved() const | |||
| bool CodeGenerator::CustomCodeList::needsSaving() const | |||
| { | |||
| for (int i = sectionContent.size(); --i >= 0;) | |||
| if (sectionContent.getUnchecked(i)->getDocument().hasChangedSinceSavePoint()) | |||
| return true; | |||
| return false; | |||
| } | |||
| int CodeGenerator::CustomCodeList::getNumSections() const | |||
| { | |||
| return sectionNames.size(); | |||
| } | |||
| const String CodeGenerator::CustomCodeList::getSectionName (int index) const | |||
| { | |||
| return sectionNames [index]; | |||
| } | |||
| const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr CodeGenerator::CustomCodeList::getDocument (int index) const | |||
| { | |||
| return true; //xxx | |||
| return sectionContent [index]; | |||
| } | |||
| CodeDocument* CodeGenerator::CustomisedCodeSnippets::getDocumentFor (const String& sectionName, bool createIfNotFound) | |||
| const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr CodeGenerator::CustomCodeList::getDocumentFor (const String& sectionName, bool createIfNotFound) | |||
| { | |||
| const int index = sectionNames.indexOf (sectionName); | |||
| @@ -415,24 +502,26 @@ CodeDocument* CodeGenerator::CustomisedCodeSnippets::getDocumentFor (const Strin | |||
| if (createIfNotFound) | |||
| { | |||
| sectionNames.add (sectionName); | |||
| sectionContent.add (new CodeDocument()); | |||
| return sectionContent.getLast(); | |||
| const CodeDocumentRef::Ptr doc (new CodeDocumentRef (new CodeDocument())); | |||
| sectionContent.add (doc); | |||
| return doc; | |||
| } | |||
| return 0; | |||
| } | |||
| const String CodeGenerator::CustomisedCodeSnippets::getSectionContent (const String& sectionName) const | |||
| const String CodeGenerator::CustomCodeList::getSectionContent (const String& sectionName) const | |||
| { | |||
| const int index = sectionNames.indexOf (sectionName); | |||
| if (index >= 0) | |||
| return sectionContent[index]->getAllContent(); | |||
| return sectionContent[index]->getDocument().getAllContent(); | |||
| return String::empty; | |||
| } | |||
| void CodeGenerator::CustomisedCodeSnippets::removeSection (const String& sectionName) | |||
| void CodeGenerator::CustomCodeList::removeSection (const String& sectionName) | |||
| { | |||
| const int index = sectionNames.indexOf (sectionName); | |||
| @@ -75,23 +75,61 @@ public: | |||
| //============================================================================== | |||
| // An object to load and store all the user-defined bits of code as documents. | |||
| class CustomisedCodeSnippets | |||
| class CustomCodeList : public ChangeBroadcaster | |||
| { | |||
| public: | |||
| CustomisedCodeSnippets(); | |||
| ~CustomisedCodeSnippets(); | |||
| CustomCodeList(); | |||
| ~CustomCodeList(); | |||
| void reloadFrom (const String& fileContent); | |||
| void applyTo (String& fileContent) const; | |||
| bool areAnySnippetsUnsaved() const; | |||
| bool needsSaving() const; | |||
| CodeDocument* getDocumentFor (const String& sectionName, bool createIfNotFound); | |||
| //============================================================================== | |||
| // Ref-counted wrapper for a code document.. | |||
| class CodeDocumentRef : public ReferenceCountedObject | |||
| { | |||
| public: | |||
| CodeDocumentRef (CodeDocument* doc_) : doc (doc_) {} | |||
| CodeDocument& getDocument() const throw() { return *doc; } | |||
| typedef ReferenceCountedObjectPtr<CodeDocumentRef> Ptr; | |||
| private: | |||
| CodeDocument* doc; | |||
| }; | |||
| //============================================================================== | |||
| int getNumSections() const; | |||
| const String getSectionName (int index) const; | |||
| const CodeDocumentRef::Ptr getDocument (int index) const; | |||
| const CodeDocumentRef::Ptr getDocumentFor (const String& sectionName, bool createIfNotFound); | |||
| const String getSectionContent (const String& sectionName) const; | |||
| void removeSection (const String& sectionName); | |||
| class Iterator | |||
| { | |||
| public: | |||
| Iterator (const String& documentText, CustomCodeList& customCode); | |||
| ~Iterator(); | |||
| bool next(); | |||
| String textBefore, textAfter, sectionName; | |||
| CodeDocumentRef::Ptr codeDocument; | |||
| private: | |||
| CustomCodeList& customCode; | |||
| StringArray lines; | |||
| int i; | |||
| }; | |||
| private: | |||
| StringArray sectionNames; | |||
| OwnedArray <CodeDocument> sectionContent; | |||
| ReferenceCountedArray <CodeDocumentRef> sectionContent; | |||
| }; | |||
| //============================================================================== | |||
| @@ -80,19 +80,24 @@ void ComponentDocument::beginNewTransaction() | |||
| undoManager.beginNewTransaction(); | |||
| } | |||
| void ComponentDocument::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) | |||
| void ComponentDocument::changed() | |||
| { | |||
| changedSinceSaved = true; | |||
| } | |||
| void ComponentDocument::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) | |||
| { | |||
| changed(); | |||
| } | |||
| void ComponentDocument::valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) | |||
| { | |||
| changedSinceSaved = true; | |||
| changed(); | |||
| } | |||
| void ComponentDocument::valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) | |||
| { | |||
| changedSinceSaved = true; | |||
| changed(); | |||
| } | |||
| bool ComponentDocument::isComponentFile (const File& file) | |||
| @@ -114,6 +119,9 @@ bool ComponentDocument::isComponentFile (const File& file) | |||
| return false; | |||
| } | |||
| const String ComponentDocument::getCppTemplate() const { return String (BinaryData::jucer_ComponentTemplate_cpp); } | |||
| const String ComponentDocument::getHeaderTemplate() const { return String (BinaryData::jucer_ComponentTemplate_h); } | |||
| void ComponentDocument::writeCode (OutputStream& cpp, OutputStream& header) | |||
| { | |||
| CodeGenerator codeGen; | |||
| @@ -128,20 +136,20 @@ void ComponentDocument::writeCode (OutputStream& cpp, OutputStream& header) | |||
| } | |||
| { | |||
| String code (BinaryData::jucer_ComponentTemplate_cpp); | |||
| String code (getCppTemplate()); | |||
| String oldContent; | |||
| codeGen.applyToCode (code, cppFile, false, project); | |||
| customisedCodeSnippets.applyTo (code); | |||
| customCode.applyTo (code); | |||
| cpp << code; | |||
| } | |||
| { | |||
| String code (BinaryData::jucer_ComponentTemplate_h); | |||
| String code (getHeaderTemplate()); | |||
| String oldContent; | |||
| codeGen.applyToCode (code, cppFile.withFileExtension (".h"), false, project); | |||
| customisedCodeSnippets.applyTo (code); | |||
| customCode.applyTo (code); | |||
| header << code; | |||
| } | |||
| } | |||
| @@ -224,7 +232,7 @@ bool ComponentDocument::reload() | |||
| undoManager.clearUndoHistory(); | |||
| changedSinceSaved = false; | |||
| customisedCodeSnippets.reloadFrom (cppFile.loadFileAsString()); | |||
| customCode.reloadFrom (cppFile.loadFileAsString()); | |||
| return true; | |||
| } | |||
| } | |||
| @@ -234,7 +242,7 @@ bool ComponentDocument::reload() | |||
| bool ComponentDocument::hasChangedSinceLastSave() | |||
| { | |||
| return changedSinceSaved; | |||
| return changedSinceSaved || customCode.needsSaving(); | |||
| } | |||
| void ComponentDocument::createSubTreeIfNotThere (const String& name) | |||
| @@ -47,6 +47,7 @@ public: | |||
| bool save(); | |||
| bool reload(); | |||
| bool hasChangedSinceLastSave(); | |||
| void changed(); | |||
| const File getCppFile() const { return cppFile; } | |||
| //============================================================================== | |||
| @@ -136,7 +137,10 @@ public: | |||
| void endDrag (const MouseEvent& e); | |||
| //============================================================================== | |||
| CodeGenerator::CustomisedCodeSnippets& getCustomisedCodeSnippets() throw() { return customisedCodeSnippets; } | |||
| CodeGenerator::CustomCodeList& getCustomCodeList() throw() { return customCode; } | |||
| const String getCppTemplate() const; | |||
| const String getHeaderTemplate() const; | |||
| //============================================================================== | |||
| ValueTree& getRoot() { return root; } | |||
| @@ -164,7 +168,7 @@ private: | |||
| File cppFile; | |||
| ValueTree root; | |||
| ScopedPointer<MarkerList> markersX, markersY; | |||
| CodeGenerator::CustomisedCodeSnippets customisedCodeSnippets; | |||
| CodeGenerator::CustomCodeList customCode; | |||
| mutable UndoManager undoManager; | |||
| bool changedSinceSaved; | |||
| @@ -536,14 +536,158 @@ public: | |||
| CodeEditorHolder (ComponentEditor& editor_) | |||
| : editor (editor_) | |||
| { | |||
| addAndMakeVisible (viewport = new Viewport()); | |||
| viewport->setScrollBarsShown (true, false); | |||
| viewport->setViewedComponent (new ContentHolder (editor)); | |||
| } | |||
| ~CodeEditorHolder() | |||
| { | |||
| } | |||
| void resized() | |||
| { | |||
| viewport->setBounds (getLocalBounds()); | |||
| int visWidth = viewport->getMaximumVisibleWidth(); | |||
| dynamic_cast <ContentHolder*> (viewport->getViewedComponent())->updateSize (visWidth); | |||
| if (viewport->getMaximumVisibleWidth() != visWidth) | |||
| dynamic_cast <ContentHolder*> (viewport->getViewedComponent())->updateSize (viewport->getMaximumVisibleWidth()); | |||
| } | |||
| private: | |||
| ComponentEditor& editor; | |||
| enum { updateCommandId = 0x23427fa1 }; | |||
| class EditorHolder : public Component, | |||
| public CodeDocument::Listener | |||
| { | |||
| public: | |||
| EditorHolder (const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr doc, | |||
| const String& name, const String& textBefore, const String& textAfter) | |||
| : Component (name), document (doc), cppTokeniser(), codeEditor (doc->getDocument(), &cppTokeniser) | |||
| { | |||
| linesBefore.addLines (textBefore); | |||
| linesAfter.addLines (textAfter); | |||
| addAndMakeVisible (&codeEditor); | |||
| doc->getDocument().addListener (this); | |||
| } | |||
| ~EditorHolder() | |||
| { | |||
| document->getDocument().removeListener (this); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.setFont (codeEditor.getFont()); | |||
| g.setColour (Colours::darkgrey); | |||
| const int fontHeight = codeEditor.getLineHeight(); | |||
| const int fontAscent = (int) codeEditor.getFont().getAscent(); | |||
| const int textX = 5; | |||
| int i; | |||
| for (i = 0; i < linesBefore.size(); ++i) | |||
| g.drawSingleLineText (linesBefore[i], textX, i * fontHeight + fontAscent); | |||
| for (i = 0; i < linesAfter.size(); ++i) | |||
| g.drawSingleLineText (linesAfter[i], textX, codeEditor.getBottom() + i * fontHeight + fontAscent); | |||
| } | |||
| void updateSize (int width) | |||
| { | |||
| const int fontHeight = codeEditor.getLineHeight(); | |||
| codeEditor.setBounds (0, fontHeight * linesBefore.size() + 1, | |||
| width, 2 + codeEditor.getScrollbarThickness() | |||
| + fontHeight * jmax (1, document->getDocument().getNumLines())); | |||
| setSize (width, (linesBefore.size() + linesAfter.size()) * fontHeight + codeEditor.getHeight()); | |||
| } | |||
| void codeDocumentChanged (const CodeDocument::Position&, const CodeDocument::Position&) | |||
| { | |||
| int oldHeight = getHeight(); | |||
| updateSize (getWidth()); | |||
| if (getHeight() != oldHeight) | |||
| getParentComponent()->handleCommandMessage (updateCommandId); | |||
| } | |||
| const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr document; | |||
| CPlusPlusCodeTokeniser cppTokeniser; | |||
| CodeEditorComponent codeEditor; | |||
| StringArray linesBefore, linesAfter; | |||
| }; | |||
| class ContentHolder : public Component, | |||
| public ChangeListener | |||
| { | |||
| public: | |||
| ContentHolder (ComponentEditor& editor_) | |||
| : editor (editor_) | |||
| { | |||
| setOpaque (true); | |||
| editor.getDocument().getCustomCodeList().addChangeListener (this); | |||
| changeListenerCallback (0); | |||
| } | |||
| ~ContentHolder() | |||
| { | |||
| editor.getDocument().getCustomCodeList().removeChangeListener (this); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| g.fillAll (Colours::lightgrey); | |||
| } | |||
| void updateSize (int width) | |||
| { | |||
| int y = 2; | |||
| for (int i = 0; i < editors.size(); ++i) | |||
| { | |||
| editors.getUnchecked(i)->updateSize (width - 8); | |||
| editors.getUnchecked(i)->setTopLeftPosition (4, y + 1); | |||
| y = editors.getUnchecked(i)->getBottom() + 1; | |||
| } | |||
| setSize (width, y + 2); | |||
| } | |||
| void changeListenerCallback (void*) | |||
| { | |||
| editors.clear(); | |||
| CodeGenerator::CustomCodeList::Iterator iter (editor.getDocument().getCppTemplate(), | |||
| editor.getDocument().getCustomCodeList()); | |||
| while (iter.next()) | |||
| { | |||
| EditorHolder* ed = new EditorHolder (iter.codeDocument, iter.sectionName, iter.textBefore, iter.textAfter); | |||
| editors.add (ed); | |||
| addAndMakeVisible (ed); | |||
| } | |||
| updateSize (getWidth()); | |||
| } | |||
| void handleCommandMessage (int commandId) | |||
| { | |||
| if (commandId == updateCommandId) | |||
| updateSize (getWidth()); | |||
| else | |||
| Component::handleCommandMessage (commandId); | |||
| } | |||
| OwnedArray <EditorHolder> editors; | |||
| ComponentEditor& editor; | |||
| }; | |||
| Viewport* viewport; | |||
| }; | |||
| //============================================================================== | |||
| @@ -43436,6 +43436,7 @@ void CodeDocument::Position::setPositionMaintained (const bool isMaintained) thr | |||
| } | |||
| else | |||
| { | |||
| // If this happens, you may have deleted the document while there are Position objects that are still using it... | |||
| jassert (owner->positionsToMaintain.contains (this)); | |||
| owner->positionsToMaintain.removeValue (this); | |||
| } | |||
| @@ -43949,7 +43950,8 @@ class CodeEditorComponent::CaretComponent : public Component, | |||
| public Timer | |||
| { | |||
| public: | |||
| CaretComponent() | |||
| CaretComponent (CodeEditorComponent& owner_) | |||
| : owner (owner_) | |||
| { | |||
| setAlwaysOnTop (true); | |||
| setInterceptsMouseClicks (false, false); | |||
| @@ -43961,27 +43963,29 @@ public: | |||
| void paint (Graphics& g) | |||
| { | |||
| if (getParentComponent()->hasKeyboardFocus (true)) | |||
| g.fillAll (findColour (CodeEditorComponent::caretColourId)); | |||
| g.fillAll (findColour (CodeEditorComponent::caretColourId)); | |||
| } | |||
| void timerCallback() | |||
| { | |||
| setVisible (! isVisible()); | |||
| setVisible (shouldBeShown() && ! isVisible()); | |||
| } | |||
| void updatePosition (CodeEditorComponent& owner) | |||
| void updatePosition() | |||
| { | |||
| startTimer (400); | |||
| setVisible (true); | |||
| setVisible (shouldBeShown()); | |||
| const Rectangle<int> pos (owner.getCharacterBounds (owner.getCaretPos())); | |||
| setBounds (pos.getX(), pos.getY(), 2, pos.getHeight()); | |||
| setBounds (owner.getCharacterBounds (owner.getCaretPos()).withWidth (2)); | |||
| } | |||
| private: | |||
| CodeEditorComponent& owner; | |||
| CaretComponent (const CaretComponent&); | |||
| CaretComponent& operator= (const CaretComponent&); | |||
| bool shouldBeShown() const { return owner.hasKeyboardFocus (true); } | |||
| }; | |||
| class CodeEditorComponent::CodeEditorLine | |||
| @@ -44224,7 +44228,7 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& document_, | |||
| addAndMakeVisible (horizontalScrollBar = new ScrollBar (false)); | |||
| horizontalScrollBar->setSingleStepSize (1.0); | |||
| addAndMakeVisible (caret = new CaretComponent()); | |||
| addAndMakeVisible (caret = new CaretComponent (*this)); | |||
| Font f (12.0f); | |||
| f.setTypefaceName (Font::getDefaultMonospacedFontName()); | |||
| @@ -44262,7 +44266,7 @@ void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& aff | |||
| triggerAsyncUpdate(); | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| columnToTryToMaintain = -1; | |||
| if (affectedTextEnd.getPosition() >= selectionStart.getPosition() | |||
| @@ -44282,7 +44286,7 @@ void CodeEditorComponent::resized() | |||
| columnsOnScreen = (int) ((getWidth() - scrollbarThickness) / charWidth); | |||
| lines.clear(); | |||
| rebuildLineTokens(); | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| verticalScrollBar->setBounds (getWidth() - scrollbarThickness, 0, scrollbarThickness, getHeight() - scrollbarThickness); | |||
| horizontalScrollBar->setBounds (gutter, getHeight() - scrollbarThickness, getWidth() - scrollbarThickness - gutter, scrollbarThickness); | |||
| @@ -44424,7 +44428,7 @@ void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, con | |||
| deselectAll(); | |||
| } | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| scrollToKeepCaretOnScreen(); | |||
| updateScrollBars(); | |||
| } | |||
| @@ -44455,7 +44459,7 @@ void CodeEditorComponent::scrollToLineInternal (int newFirstLineOnScreen) | |||
| if (newFirstLineOnScreen != firstLineOnScreen) | |||
| { | |||
| firstLineOnScreen = newFirstLineOnScreen; | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| updateCachedIterators (firstLineOnScreen); | |||
| triggerAsyncUpdate(); | |||
| @@ -44469,7 +44473,7 @@ void CodeEditorComponent::scrollToColumnInternal (double column) | |||
| if (xOffset != newOffset) | |||
| { | |||
| xOffset = newOffset; | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| repaint(); | |||
| } | |||
| } | |||
| @@ -44967,8 +44971,16 @@ void CodeEditorComponent::mouseDoubleClick (const MouseEvent& e) | |||
| void CodeEditorComponent::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) | |||
| { | |||
| verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); | |||
| horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); | |||
| if ((verticalScrollBar->isVisible() && wheelIncrementY != 0) | |||
| || (horizontalScrollBar->isVisible() && wheelIncrementX != 0)) | |||
| { | |||
| verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); | |||
| horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); | |||
| } | |||
| else | |||
| { | |||
| Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); | |||
| } | |||
| } | |||
| void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) | |||
| @@ -44979,6 +44991,16 @@ void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, doub | |||
| scrollToColumnInternal (newRangeStart); | |||
| } | |||
| void CodeEditorComponent::focusGained (FocusChangeType cause) | |||
| { | |||
| caret->updatePosition(); | |||
| } | |||
| void CodeEditorComponent::focusLost (FocusChangeType cause) | |||
| { | |||
| caret->updatePosition(); | |||
| } | |||
| void CodeEditorComponent::setTabSize (const int numSpaces, const bool insertSpaces) throw() | |||
| { | |||
| useSpacesForTabs = insertSpaces; | |||
| @@ -240061,15 +240083,19 @@ void UIViewComponentPeer::toFront (bool makeActiveWindow) | |||
| void UIViewComponentPeer::toBehind (ComponentPeer* other) | |||
| { | |||
| UIViewComponentPeer* o = (UIViewComponentPeer*) other; | |||
| UIViewComponentPeer* const otherPeer = dynamic_cast <UIViewComponentPeer*> (other); | |||
| jassert (otherPeer != 0); // wrong type of window? | |||
| if (isSharedWindow) | |||
| if (otherPeer != 0) | |||
| { | |||
| [[view superview] insertSubview: view belowSubview: o->view]; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse // don't know how to do this | |||
| if (isSharedWindow) | |||
| { | |||
| [[view superview] insertSubview: view belowSubview: otherPeer->view]; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse // don't know how to do this | |||
| } | |||
| } | |||
| } | |||
| @@ -245320,19 +245346,23 @@ void NSViewComponentPeer::toFront (bool makeActiveWindow) | |||
| void NSViewComponentPeer::toBehind (ComponentPeer* other) | |||
| { | |||
| NSViewComponentPeer* o = (NSViewComponentPeer*) other; | |||
| NSViewComponentPeer* const otherPeer = dynamic_cast <NSViewComponentPeer*> (other); | |||
| jassert (otherPeer != 0); // wrong type of window? | |||
| if (isSharedWindow) | |||
| if (otherPeer != 0) | |||
| { | |||
| [[view superview] addSubview: view | |||
| positioned: NSWindowBelow | |||
| relativeTo: o->view]; | |||
| } | |||
| else | |||
| { | |||
| [window orderWindow: NSWindowBelow | |||
| relativeTo: o->window != 0 ? [o->window windowNumber] | |||
| : nil ]; | |||
| if (isSharedWindow) | |||
| { | |||
| [[view superview] addSubview: view | |||
| positioned: NSWindowBelow | |||
| relativeTo: otherPeer->view]; | |||
| } | |||
| else | |||
| { | |||
| [window orderWindow: NSWindowBelow | |||
| relativeTo: otherPeer->window != 0 ? [otherPeer->window windowNumber] | |||
| : nil ]; | |||
| } | |||
| } | |||
| } | |||
| @@ -10313,6 +10313,14 @@ public: | |||
| void setHeight (const ValueType newHeight) throw() { h = newHeight; } | |||
| const Rectangle withX (const ValueType newX) const throw() { return Rectangle (newX, y, w, h); } | |||
| const Rectangle withY (const ValueType newY) const throw() { return Rectangle (x, newY, w, h); } | |||
| const Rectangle withWidth (const ValueType newWidth) const throw() { return Rectangle (x, y, newWidth, h); } | |||
| const Rectangle withHeight (const ValueType newHeight) const throw() { return Rectangle (x, y, w, newHeight); } | |||
| void setLeft (const ValueType newLeft) throw() | |||
| { | |||
| w = jmax (ValueType(), x + w - newLeft); | |||
| @@ -21156,6 +21164,8 @@ public: | |||
| void setFont (const Font& newFont); | |||
| const Font& getFont() const throw() { return font; } | |||
| void resetToDefaultColours(); | |||
| void setColourForTokenType (int tokenType, const Colour& colour); | |||
| @@ -21174,6 +21184,8 @@ public: | |||
| void setScrollbarThickness (int thickness) throw(); | |||
| int getScrollbarThickness() const throw() { return scrollbarThickness; } | |||
| void resized(); | |||
| void paint (Graphics& g); | |||
| bool keyPressed (const KeyPress& key); | |||
| @@ -21182,6 +21194,8 @@ public: | |||
| void mouseUp (const MouseEvent& e); | |||
| void mouseDoubleClick (const MouseEvent& e); | |||
| void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); | |||
| void focusGained (FocusChangeType cause); | |||
| void focusLost (FocusChangeType cause); | |||
| void timerCallback(); | |||
| void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart); | |||
| void handleAsyncUpdate(); | |||
| @@ -431,6 +431,7 @@ void CodeDocument::Position::setPositionMaintained (const bool isMaintained) thr | |||
| } | |||
| else | |||
| { | |||
| // If this happens, you may have deleted the document while there are Position objects that are still using it... | |||
| jassert (owner->positionsToMaintain.contains (this)); | |||
| owner->positionsToMaintain.removeValue (this); | |||
| } | |||
| @@ -37,7 +37,8 @@ class CodeEditorComponent::CaretComponent : public Component, | |||
| public Timer | |||
| { | |||
| public: | |||
| CaretComponent() | |||
| CaretComponent (CodeEditorComponent& owner_) | |||
| : owner (owner_) | |||
| { | |||
| setAlwaysOnTop (true); | |||
| setInterceptsMouseClicks (false, false); | |||
| @@ -49,27 +50,29 @@ public: | |||
| void paint (Graphics& g) | |||
| { | |||
| if (getParentComponent()->hasKeyboardFocus (true)) | |||
| g.fillAll (findColour (CodeEditorComponent::caretColourId)); | |||
| g.fillAll (findColour (CodeEditorComponent::caretColourId)); | |||
| } | |||
| void timerCallback() | |||
| { | |||
| setVisible (! isVisible()); | |||
| setVisible (shouldBeShown() && ! isVisible()); | |||
| } | |||
| void updatePosition (CodeEditorComponent& owner) | |||
| void updatePosition() | |||
| { | |||
| startTimer (400); | |||
| setVisible (true); | |||
| setVisible (shouldBeShown()); | |||
| const Rectangle<int> pos (owner.getCharacterBounds (owner.getCaretPos())); | |||
| setBounds (pos.getX(), pos.getY(), 2, pos.getHeight()); | |||
| setBounds (owner.getCharacterBounds (owner.getCaretPos()).withWidth (2)); | |||
| } | |||
| private: | |||
| CodeEditorComponent& owner; | |||
| CaretComponent (const CaretComponent&); | |||
| CaretComponent& operator= (const CaretComponent&); | |||
| bool shouldBeShown() const { return owner.hasKeyboardFocus (true); } | |||
| }; | |||
| //============================================================================== | |||
| @@ -314,7 +317,7 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& document_, | |||
| addAndMakeVisible (horizontalScrollBar = new ScrollBar (false)); | |||
| horizontalScrollBar->setSingleStepSize (1.0); | |||
| addAndMakeVisible (caret = new CaretComponent()); | |||
| addAndMakeVisible (caret = new CaretComponent (*this)); | |||
| Font f (12.0f); | |||
| f.setTypefaceName (Font::getDefaultMonospacedFontName()); | |||
| @@ -353,7 +356,7 @@ void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& aff | |||
| triggerAsyncUpdate(); | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| columnToTryToMaintain = -1; | |||
| if (affectedTextEnd.getPosition() >= selectionStart.getPosition() | |||
| @@ -373,7 +376,7 @@ void CodeEditorComponent::resized() | |||
| columnsOnScreen = (int) ((getWidth() - scrollbarThickness) / charWidth); | |||
| lines.clear(); | |||
| rebuildLineTokens(); | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| verticalScrollBar->setBounds (getWidth() - scrollbarThickness, 0, scrollbarThickness, getHeight() - scrollbarThickness); | |||
| horizontalScrollBar->setBounds (gutter, getHeight() - scrollbarThickness, getWidth() - scrollbarThickness - gutter, scrollbarThickness); | |||
| @@ -516,7 +519,7 @@ void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, con | |||
| deselectAll(); | |||
| } | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| scrollToKeepCaretOnScreen(); | |||
| updateScrollBars(); | |||
| } | |||
| @@ -547,7 +550,7 @@ void CodeEditorComponent::scrollToLineInternal (int newFirstLineOnScreen) | |||
| if (newFirstLineOnScreen != firstLineOnScreen) | |||
| { | |||
| firstLineOnScreen = newFirstLineOnScreen; | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| updateCachedIterators (firstLineOnScreen); | |||
| triggerAsyncUpdate(); | |||
| @@ -561,7 +564,7 @@ void CodeEditorComponent::scrollToColumnInternal (double column) | |||
| if (xOffset != newOffset) | |||
| { | |||
| xOffset = newOffset; | |||
| caret->updatePosition (*this); | |||
| caret->updatePosition(); | |||
| repaint(); | |||
| } | |||
| } | |||
| @@ -1071,8 +1074,16 @@ void CodeEditorComponent::mouseDoubleClick (const MouseEvent& e) | |||
| void CodeEditorComponent::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) | |||
| { | |||
| verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); | |||
| horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); | |||
| if ((verticalScrollBar->isVisible() && wheelIncrementY != 0) | |||
| || (horizontalScrollBar->isVisible() && wheelIncrementX != 0)) | |||
| { | |||
| verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); | |||
| horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); | |||
| } | |||
| else | |||
| { | |||
| Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); | |||
| } | |||
| } | |||
| void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) | |||
| @@ -1083,6 +1094,17 @@ void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, doub | |||
| scrollToColumnInternal (newRangeStart); | |||
| } | |||
| //============================================================================== | |||
| void CodeEditorComponent::focusGained (FocusChangeType cause) | |||
| { | |||
| caret->updatePosition(); | |||
| } | |||
| void CodeEditorComponent::focusLost (FocusChangeType cause) | |||
| { | |||
| caret->updatePosition(); | |||
| } | |||
| //============================================================================== | |||
| void CodeEditorComponent::setTabSize (const int numSpaces, const bool insertSpaces) throw() | |||
| { | |||
| @@ -175,6 +175,9 @@ public: | |||
| */ | |||
| void setFont (const Font& newFont); | |||
| /** Returns the font that the editor is using. */ | |||
| const Font& getFont() const throw() { return font; } | |||
| /** Resets the syntax highlighting colours to the default ones provided by the | |||
| code tokeniser. | |||
| @see CodeTokeniser::getDefaultColour | |||
| @@ -217,6 +220,9 @@ public: | |||
| /** Changes the size of the scrollbars. */ | |||
| void setScrollbarThickness (int thickness) throw(); | |||
| /** Returns the thickness of the scrollbars. */ | |||
| int getScrollbarThickness() const throw() { return scrollbarThickness; } | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void resized(); | |||
| @@ -235,6 +241,10 @@ public: | |||
| /** @internal */ | |||
| void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); | |||
| /** @internal */ | |||
| void focusGained (FocusChangeType cause); | |||
| /** @internal */ | |||
| void focusLost (FocusChangeType cause); | |||
| /** @internal */ | |||
| void timerCallback(); | |||
| /** @internal */ | |||
| void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart); | |||
| @@ -161,6 +161,18 @@ public: | |||
| /** Changes the rectangle's height */ | |||
| void setHeight (const ValueType newHeight) throw() { h = newHeight; } | |||
| /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ | |||
| const Rectangle withX (const ValueType newX) const throw() { return Rectangle (newX, y, w, h); } | |||
| /** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */ | |||
| const Rectangle withY (const ValueType newY) const throw() { return Rectangle (x, newY, w, h); } | |||
| /** Returns a rectangle which has the same position and height as this one, but with a different width. */ | |||
| const Rectangle withWidth (const ValueType newWidth) const throw() { return Rectangle (x, y, newWidth, h); } | |||
| /** Returns a rectangle which has the same position and width as this one, but with a different height. */ | |||
| const Rectangle withHeight (const ValueType newHeight) const throw() { return Rectangle (x, y, w, newHeight); } | |||
| /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. | |||
| If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. | |||
| */ | |||
| @@ -592,15 +592,19 @@ void UIViewComponentPeer::toFront (bool makeActiveWindow) | |||
| void UIViewComponentPeer::toBehind (ComponentPeer* other) | |||
| { | |||
| UIViewComponentPeer* o = (UIViewComponentPeer*) other; | |||
| UIViewComponentPeer* const otherPeer = dynamic_cast <UIViewComponentPeer*> (other); | |||
| jassert (otherPeer != 0); // wrong type of window? | |||
| if (isSharedWindow) | |||
| if (otherPeer != 0) | |||
| { | |||
| [[view superview] insertSubview: view belowSubview: o->view]; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse // don't know how to do this | |||
| if (isSharedWindow) | |||
| { | |||
| [[view superview] insertSubview: view belowSubview: otherPeer->view]; | |||
| } | |||
| else | |||
| { | |||
| jassertfalse // don't know how to do this | |||
| } | |||
| } | |||
| } | |||
| @@ -1178,19 +1178,23 @@ void NSViewComponentPeer::toFront (bool makeActiveWindow) | |||
| void NSViewComponentPeer::toBehind (ComponentPeer* other) | |||
| { | |||
| NSViewComponentPeer* o = (NSViewComponentPeer*) other; | |||
| NSViewComponentPeer* const otherPeer = dynamic_cast <NSViewComponentPeer*> (other); | |||
| jassert (otherPeer != 0); // wrong type of window? | |||
| if (isSharedWindow) | |||
| { | |||
| [[view superview] addSubview: view | |||
| positioned: NSWindowBelow | |||
| relativeTo: o->view]; | |||
| } | |||
| else | |||
| if (otherPeer != 0) | |||
| { | |||
| [window orderWindow: NSWindowBelow | |||
| relativeTo: o->window != 0 ? [o->window windowNumber] | |||
| : nil ]; | |||
| if (isSharedWindow) | |||
| { | |||
| [[view superview] addSubview: view | |||
| positioned: NSWindowBelow | |||
| relativeTo: otherPeer->view]; | |||
| } | |||
| else | |||
| { | |||
| [window orderWindow: NSWindowBelow | |||
| relativeTo: otherPeer->window != 0 ? [otherPeer->window windowNumber] | |||
| : nil ]; | |||
| } | |||
| } | |||
| } | |||