fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,942 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class CodeDocumentLine
{
public:
CodeDocumentLine (const String::CharPointerType startOfLine,
const String::CharPointerType endOfLine,
const int lineLen,
const int numNewLineChars,
const int startInFile)
: line (startOfLine, endOfLine),
lineStartInFile (startInFile),
lineLength (lineLen),
lineLengthWithoutNewLines (lineLen - numNewLineChars)
{
}
static void createLines (Array<CodeDocumentLine*>& newLines, StringRef text)
{
auto t = text.text;
int charNumInFile = 0;
bool finished = false;
while (! (finished || t.isEmpty()))
{
auto startOfLine = t;
auto startOfLineInFile = charNumInFile;
int lineLength = 0;
int numNewLineChars = 0;
for (;;)
{
auto c = t.getAndAdvance();
if (c == 0)
{
finished = true;
break;
}
++charNumInFile;
++lineLength;
if (c == '\r')
{
++numNewLineChars;
if (*t == '\n')
{
++t;
++charNumInFile;
++lineLength;
++numNewLineChars;
}
break;
}
if (c == '\n')
{
++numNewLineChars;
break;
}
}
newLines.add (new CodeDocumentLine (startOfLine, t, lineLength,
numNewLineChars, startOfLineInFile));
}
jassert (charNumInFile == text.length());
}
bool endsWithLineBreak() const noexcept
{
return lineLengthWithoutNewLines != lineLength;
}
void updateLength() noexcept
{
lineLength = 0;
lineLengthWithoutNewLines = 0;
for (auto t = line.getCharPointer();;)
{
auto c = t.getAndAdvance();
if (c == 0)
break;
++lineLength;
if (c != '\n' && c != '\r')
lineLengthWithoutNewLines = lineLength;
}
}
String line;
int lineStartInFile, lineLength, lineLengthWithoutNewLines;
};
//==============================================================================
CodeDocument::Iterator::Iterator (const CodeDocument& doc) noexcept : document (&doc) {}
CodeDocument::Iterator::~Iterator() noexcept {}
juce_wchar CodeDocument::Iterator::nextChar() noexcept
{
for (;;)
{
if (charPointer.getAddress() == nullptr)
{
if (auto* l = document->lines[line])
charPointer = l->line.getCharPointer();
else
return 0;
}
if (auto result = charPointer.getAndAdvance())
{
if (charPointer.isEmpty())
{
++line;
charPointer = nullptr;
}
++position;
return result;
}
++line;
charPointer = nullptr;
}
}
void CodeDocument::Iterator::skip() noexcept
{
nextChar();
}
void CodeDocument::Iterator::skipToEndOfLine() noexcept
{
if (charPointer.getAddress() == nullptr)
{
if (auto* l = document->lines[line])
charPointer = l->line.getCharPointer();
else
return;
}
position += (int) charPointer.length();
++line;
charPointer = nullptr;
}
juce_wchar CodeDocument::Iterator::peekNextChar() const noexcept
{
if (charPointer.getAddress() == nullptr)
{
if (auto* l = document->lines[line])
charPointer = l->line.getCharPointer();
else
return 0;
}
if (auto c = *charPointer)
return c;
if (auto* l = document->lines [line + 1])
return l->line[0];
return 0;
}
void CodeDocument::Iterator::skipWhitespace() noexcept
{
while (CharacterFunctions::isWhitespace (peekNextChar()))
skip();
}
bool CodeDocument::Iterator::isEOF() const noexcept
{
return charPointer.getAddress() == nullptr && line >= document->lines.size();
}
//==============================================================================
CodeDocument::Position::Position() noexcept
{
}
CodeDocument::Position::Position (const CodeDocument& ownerDocument,
const int lineNum, const int index) noexcept
: owner (const_cast<CodeDocument*> (&ownerDocument)),
line (lineNum), indexInLine (index)
{
setLineAndIndex (lineNum, index);
}
CodeDocument::Position::Position (const CodeDocument& ownerDocument, int pos) noexcept
: owner (const_cast<CodeDocument*> (&ownerDocument))
{
setPosition (pos);
}
CodeDocument::Position::Position (const Position& other) noexcept
: owner (other.owner), characterPos (other.characterPos), line (other.line),
indexInLine (other.indexInLine)
{
jassert (*this == other);
}
CodeDocument::Position::~Position()
{
setPositionMaintained (false);
}
CodeDocument::Position& CodeDocument::Position::operator= (const Position& other)
{
if (this != &other)
{
const bool wasPositionMaintained = positionMaintained;
if (owner != other.owner)
setPositionMaintained (false);
owner = other.owner;
line = other.line;
indexInLine = other.indexInLine;
characterPos = other.characterPos;
setPositionMaintained (wasPositionMaintained);
jassert (*this == other);
}
return *this;
}
bool CodeDocument::Position::operator== (const Position& other) const noexcept
{
jassert ((characterPos == other.characterPos)
== (line == other.line && indexInLine == other.indexInLine));
return characterPos == other.characterPos
&& line == other.line
&& indexInLine == other.indexInLine
&& owner == other.owner;
}
bool CodeDocument::Position::operator!= (const Position& other) const noexcept
{
return ! operator== (other);
}
void CodeDocument::Position::setLineAndIndex (const int newLineNum, const int newIndexInLine)
{
jassert (owner != nullptr);
if (owner->lines.size() == 0)
{
line = 0;
indexInLine = 0;
characterPos = 0;
}
else
{
if (newLineNum >= owner->lines.size())
{
line = owner->lines.size() - 1;
auto& l = *owner->lines.getUnchecked (line);
indexInLine = l.lineLengthWithoutNewLines;
characterPos = l.lineStartInFile + indexInLine;
}
else
{
line = jmax (0, newLineNum);
auto& l = *owner->lines.getUnchecked (line);
if (l.lineLengthWithoutNewLines > 0)
indexInLine = jlimit (0, l.lineLengthWithoutNewLines, newIndexInLine);
else
indexInLine = 0;
characterPos = l.lineStartInFile + indexInLine;
}
}
}
void CodeDocument::Position::setPosition (const int newPosition)
{
jassert (owner != nullptr);
line = 0;
indexInLine = 0;
characterPos = 0;
if (newPosition > 0)
{
int lineStart = 0;
auto lineEnd = owner->lines.size();
for (;;)
{
if (lineEnd - lineStart < 4)
{
for (int i = lineStart; i < lineEnd; ++i)
{
auto& l = *owner->lines.getUnchecked (i);
auto index = newPosition - l.lineStartInFile;
if (index >= 0 && (index < l.lineLength || i == lineEnd - 1))
{
line = i;
indexInLine = jmin (l.lineLengthWithoutNewLines, index);
characterPos = l.lineStartInFile + indexInLine;
}
}
break;
}
else
{
auto midIndex = (lineStart + lineEnd + 1) / 2;
if (newPosition >= owner->lines.getUnchecked (midIndex)->lineStartInFile)
lineStart = midIndex;
else
lineEnd = midIndex;
}
}
}
}
void CodeDocument::Position::moveBy (int characterDelta)
{
jassert (owner != nullptr);
if (characterDelta == 1)
{
setPosition (getPosition());
// If moving right, make sure we don't get stuck between the \r and \n characters..
if (line < owner->lines.size())
{
auto& l = *owner->lines.getUnchecked (line);
if (indexInLine + characterDelta < l.lineLength
&& indexInLine + characterDelta >= l.lineLengthWithoutNewLines + 1)
++characterDelta;
}
}
setPosition (characterPos + characterDelta);
}
CodeDocument::Position CodeDocument::Position::movedBy (const int characterDelta) const
{
CodeDocument::Position p (*this);
p.moveBy (characterDelta);
return p;
}
CodeDocument::Position CodeDocument::Position::movedByLines (const int deltaLines) const
{
CodeDocument::Position p (*this);
p.setLineAndIndex (getLineNumber() + deltaLines, getIndexInLine());
return p;
}
juce_wchar CodeDocument::Position::getCharacter() const
{
if (auto* l = owner->lines [line])
return l->line [getIndexInLine()];
return 0;
}
String CodeDocument::Position::getLineText() const
{
if (auto* l = owner->lines [line])
return l->line;
return {};
}
void CodeDocument::Position::setPositionMaintained (const bool isMaintained)
{
if (isMaintained != positionMaintained)
{
positionMaintained = isMaintained;
if (owner != nullptr)
{
if (isMaintained)
{
jassert (! owner->positionsToMaintain.contains (this));
owner->positionsToMaintain.add (this);
}
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.removeFirstMatchingValue (this);
}
}
}
}
//==============================================================================
CodeDocument::CodeDocument() : undoManager (std::numeric_limits<int>::max(), 10000)
{
}
CodeDocument::~CodeDocument()
{
}
String CodeDocument::getAllContent() const
{
return getTextBetween (Position (*this, 0),
Position (*this, lines.size(), 0));
}
String CodeDocument::getTextBetween (const Position& start, const Position& end) const
{
if (end.getPosition() <= start.getPosition())
return {};
auto startLine = start.getLineNumber();
auto endLine = end.getLineNumber();
if (startLine == endLine)
{
if (auto* line = lines [startLine])
return line->line.substring (start.getIndexInLine(), end.getIndexInLine());
return {};
}
MemoryOutputStream mo;
mo.preallocate ((size_t) (end.getPosition() - start.getPosition() + 4));
auto maxLine = jmin (lines.size() - 1, endLine);
for (int i = jmax (0, startLine); i <= maxLine; ++i)
{
auto& line = *lines.getUnchecked(i);
auto len = line.lineLength;
if (i == startLine)
{
auto index = start.getIndexInLine();
mo << line.line.substring (index, len);
}
else if (i == endLine)
{
len = end.getIndexInLine();
mo << line.line.substring (0, len);
}
else
{
mo << line.line;
}
}
return mo.toUTF8();
}
int CodeDocument::getNumCharacters() const noexcept
{
if (auto* lastLine = lines.getLast())
return lastLine->lineStartInFile + lastLine->lineLength;
return 0;
}
String CodeDocument::getLine (const int lineIndex) const noexcept
{
if (auto* line = lines[lineIndex])
return line->line;
return {};
}
int CodeDocument::getMaximumLineLength() noexcept
{
if (maximumLineLength < 0)
{
maximumLineLength = 0;
for (auto* l : lines)
maximumLineLength = jmax (maximumLineLength, l->lineLength);
}
return maximumLineLength;
}
void CodeDocument::deleteSection (const Position& startPosition, const Position& endPosition)
{
deleteSection (startPosition.getPosition(), endPosition.getPosition());
}
void CodeDocument::deleteSection (const int start, const int end)
{
remove (start, end, true);
}
void CodeDocument::insertText (const Position& position, const String& text)
{
insertText (position.getPosition(), text);
}
void CodeDocument::insertText (const int insertIndex, const String& text)
{
insert (text, insertIndex, true);
}
void CodeDocument::replaceSection (const int start, const int end, const String& newText)
{
insertText (end, newText);
deleteSection (start, end);
}
void CodeDocument::applyChanges (const String& newContent)
{
const String corrected (StringArray::fromLines (newContent)
.joinIntoString (newLineChars));
TextDiff diff (getAllContent(), corrected);
for (auto& c : diff.changes)
{
if (c.isDeletion())
remove (c.start, c.start + c.length, true);
else
insert (c.insertedText, c.start, true);
}
}
void CodeDocument::replaceAllContent (const String& newContent)
{
remove (0, getNumCharacters(), true);
insert (newContent, 0, true);
}
bool CodeDocument::loadFromStream (InputStream& stream)
{
remove (0, getNumCharacters(), false);
insert (stream.readEntireStreamAsString(), 0, false);
setSavePoint();
clearUndoHistory();
return true;
}
bool CodeDocument::writeToStream (OutputStream& stream)
{
for (auto* l : lines)
{
auto temp = l->line; // use a copy to avoid bloating the memory footprint of the stored string.
const char* utf8 = temp.toUTF8();
if (! stream.write (utf8, strlen (utf8)))
return false;
}
return true;
}
void CodeDocument::setNewLineCharacters (const String& newChars) noexcept
{
jassert (newChars == "\r\n" || newChars == "\n" || newChars == "\r");
newLineChars = newChars;
}
void CodeDocument::newTransaction()
{
undoManager.beginNewTransaction (String());
}
void CodeDocument::undo()
{
newTransaction();
undoManager.undo();
}
void CodeDocument::redo()
{
undoManager.redo();
}
void CodeDocument::clearUndoHistory()
{
undoManager.clearUndoHistory();
}
void CodeDocument::setSavePoint() noexcept
{
indexOfSavedState = currentActionIndex;
}
bool CodeDocument::hasChangedSinceSavePoint() const noexcept
{
return currentActionIndex != indexOfSavedState;
}
//==============================================================================
static inline int getCharacterType (juce_wchar character) noexcept
{
return (CharacterFunctions::isLetterOrDigit (character) || character == '_')
? 2 : (CharacterFunctions::isWhitespace (character) ? 0 : 1);
}
CodeDocument::Position CodeDocument::findWordBreakAfter (const Position& position) const noexcept
{
auto p = position;
const int maxDistance = 256;
int i = 0;
while (i < maxDistance
&& CharacterFunctions::isWhitespace (p.getCharacter())
&& (i == 0 || (p.getCharacter() != '\n'
&& p.getCharacter() != '\r')))
{
++i;
p.moveBy (1);
}
if (i == 0)
{
auto type = getCharacterType (p.getCharacter());
while (i < maxDistance && type == getCharacterType (p.getCharacter()))
{
++i;
p.moveBy (1);
}
while (i < maxDistance
&& CharacterFunctions::isWhitespace (p.getCharacter())
&& (i == 0 || (p.getCharacter() != '\n'
&& p.getCharacter() != '\r')))
{
++i;
p.moveBy (1);
}
}
return p;
}
CodeDocument::Position CodeDocument::findWordBreakBefore (const Position& position) const noexcept
{
auto p = position;
const int maxDistance = 256;
int i = 0;
bool stoppedAtLineStart = false;
while (i < maxDistance)
{
auto c = p.movedBy (-1).getCharacter();
if (c == '\r' || c == '\n')
{
stoppedAtLineStart = true;
if (i > 0)
break;
}
if (! CharacterFunctions::isWhitespace (c))
break;
p.moveBy (-1);
++i;
}
if (i < maxDistance && ! stoppedAtLineStart)
{
auto type = getCharacterType (p.movedBy (-1).getCharacter());
while (i < maxDistance && type == getCharacterType (p.movedBy (-1).getCharacter()))
{
p.moveBy (-1);
++i;
}
}
return p;
}
void CodeDocument::findTokenContaining (const Position& pos, Position& start, Position& end) const noexcept
{
auto isTokenCharacter = [] (juce_wchar c) { return CharacterFunctions::isLetterOrDigit (c) || c == '.' || c == '_'; };
end = pos;
while (isTokenCharacter (end.getCharacter()))
end.moveBy (1);
start = end;
while (start.getIndexInLine() > 0
&& isTokenCharacter (start.movedBy (-1).getCharacter()))
start.moveBy (-1);
}
void CodeDocument::findLineContaining (const Position& pos, Position& s, Position& e) const noexcept
{
s.setLineAndIndex (pos.getLineNumber(), 0);
e.setLineAndIndex (pos.getLineNumber() + 1, 0);
}
void CodeDocument::checkLastLineStatus()
{
while (lines.size() > 0
&& lines.getLast()->lineLength == 0
&& (lines.size() == 1 || ! lines.getUnchecked (lines.size() - 2)->endsWithLineBreak()))
{
// remove any empty lines at the end if the preceding line doesn't end in a newline.
lines.removeLast();
}
const CodeDocumentLine* const lastLine = lines.getLast();
if (lastLine != nullptr && lastLine->endsWithLineBreak())
{
// check that there's an empty line at the end if the preceding one ends in a newline..
lines.add (new CodeDocumentLine (StringRef(), StringRef(), 0, 0,
lastLine->lineStartInFile + lastLine->lineLength));
}
}
//==============================================================================
void CodeDocument::addListener (CodeDocument::Listener* l) noexcept { listeners.add (l); }
void CodeDocument::removeListener (CodeDocument::Listener* l) noexcept { listeners.remove (l); }
//==============================================================================
struct CodeDocument::InsertAction : public UndoableAction
{
InsertAction (CodeDocument& doc, const String& t, const int pos) noexcept
: owner (doc), text (t), insertPos (pos)
{
}
bool perform() override
{
owner.currentActionIndex++;
owner.insert (text, insertPos, false);
return true;
}
bool undo() override
{
owner.currentActionIndex--;
owner.remove (insertPos, insertPos + text.length(), false);
return true;
}
int getSizeInUnits() override { return text.length() + 32; }
CodeDocument& owner;
const String text;
const int insertPos;
JUCE_DECLARE_NON_COPYABLE (InsertAction)
};
void CodeDocument::insert (const String& text, const int insertPos, const bool undoable)
{
if (text.isNotEmpty())
{
if (undoable)
{
undoManager.perform (new InsertAction (*this, text, insertPos));
}
else
{
Position pos (*this, insertPos);
auto firstAffectedLine = pos.getLineNumber();
auto* firstLine = lines[firstAffectedLine];
auto textInsideOriginalLine = text;
if (firstLine != nullptr)
{
auto index = pos.getIndexInLine();
textInsideOriginalLine = firstLine->line.substring (0, index)
+ textInsideOriginalLine
+ firstLine->line.substring (index);
}
maximumLineLength = -1;
Array<CodeDocumentLine*> newLines;
CodeDocumentLine::createLines (newLines, textInsideOriginalLine);
jassert (newLines.size() > 0);
auto* newFirstLine = newLines.getUnchecked (0);
newFirstLine->lineStartInFile = firstLine != nullptr ? firstLine->lineStartInFile : 0;
lines.set (firstAffectedLine, newFirstLine);
if (newLines.size() > 1)
lines.insertArray (firstAffectedLine + 1, newLines.getRawDataPointer() + 1, newLines.size() - 1);
int lineStart = newFirstLine->lineStartInFile;
for (int i = firstAffectedLine; i < lines.size(); ++i)
{
auto& l = *lines.getUnchecked (i);
l.lineStartInFile = lineStart;
lineStart += l.lineLength;
}
checkLastLineStatus();
auto newTextLength = text.length();
for (auto* p : positionsToMaintain)
if (p->getPosition() >= insertPos)
p->setPosition (p->getPosition() + newTextLength);
listeners.call ([&] (Listener& l) { l.codeDocumentTextInserted (text, insertPos); });
}
}
}
//==============================================================================
struct CodeDocument::DeleteAction : public UndoableAction
{
DeleteAction (CodeDocument& doc, int start, int end) noexcept
: owner (doc), startPos (start), endPos (end),
removedText (doc.getTextBetween (CodeDocument::Position (doc, start),
CodeDocument::Position (doc, end)))
{
}
bool perform() override
{
owner.currentActionIndex++;
owner.remove (startPos, endPos, false);
return true;
}
bool undo() override
{
owner.currentActionIndex--;
owner.insert (removedText, startPos, false);
return true;
}
int getSizeInUnits() override { return (endPos - startPos) + 32; }
CodeDocument& owner;
const int startPos, endPos;
const String removedText;
JUCE_DECLARE_NON_COPYABLE (DeleteAction)
};
void CodeDocument::remove (const int startPos, const int endPos, const bool undoable)
{
if (endPos <= startPos)
return;
if (undoable)
{
undoManager.perform (new DeleteAction (*this, startPos, endPos));
}
else
{
Position startPosition (*this, startPos);
Position endPosition (*this, endPos);
maximumLineLength = -1;
auto firstAffectedLine = startPosition.getLineNumber();
auto endLine = endPosition.getLineNumber();
auto& firstLine = *lines.getUnchecked (firstAffectedLine);
if (firstAffectedLine == endLine)
{
firstLine.line = firstLine.line.substring (0, startPosition.getIndexInLine())
+ firstLine.line.substring (endPosition.getIndexInLine());
firstLine.updateLength();
}
else
{
auto& lastLine = *lines.getUnchecked (endLine);
firstLine.line = firstLine.line.substring (0, startPosition.getIndexInLine())
+ lastLine.line.substring (endPosition.getIndexInLine());
firstLine.updateLength();
int numLinesToRemove = endLine - firstAffectedLine;
lines.removeRange (firstAffectedLine + 1, numLinesToRemove);
}
for (int i = firstAffectedLine + 1; i < lines.size(); ++i)
{
auto& l = *lines.getUnchecked (i);
auto& previousLine = *lines.getUnchecked (i - 1);
l.lineStartInFile = previousLine.lineStartInFile + previousLine.lineLength;
}
checkLastLineStatus();
auto totalChars = getNumCharacters();
for (auto* p : positionsToMaintain)
{
if (p->getPosition() > startPosition.getPosition())
p->setPosition (jmax (startPos, p->getPosition() + startPos - endPos));
if (p->getPosition() > totalChars)
p->setPosition (totalChars);
}
listeners.call ([=] (Listener& l) { l.codeDocumentTextDeleted (startPos, endPos); });
}
}
} // namespace juce