- clean working copy for leaving SVN
This commit is contained in:
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "jucer_CodeHelpers.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace CodeHelpers
|
||||
{
|
||||
String indent (const String& code, const int numSpaces, bool indentFirstLine)
|
||||
{
|
||||
if (numSpaces == 0)
|
||||
return code;
|
||||
|
||||
const String space (String::repeatedString (" ", numSpaces));
|
||||
|
||||
StringArray lines;
|
||||
lines.addLines (code);
|
||||
|
||||
for (int i = (indentFirstLine ? 0 : 1); i < lines.size(); ++i)
|
||||
{
|
||||
String s (lines[i].trimEnd());
|
||||
if (s.isNotEmpty())
|
||||
s = space + s;
|
||||
|
||||
lines.set (i, s);
|
||||
}
|
||||
|
||||
return lines.joinIntoString (newLine);
|
||||
}
|
||||
|
||||
String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates)
|
||||
{
|
||||
if (s.isEmpty())
|
||||
return "unknown";
|
||||
|
||||
if (removeColons)
|
||||
s = s.replaceCharacters (".,;:/@", "______");
|
||||
else
|
||||
s = s.replaceCharacters (".,;/@", "_____");
|
||||
|
||||
for (int i = s.length(); --i > 0;)
|
||||
if (CharacterFunctions::isLetter (s[i])
|
||||
&& CharacterFunctions::isLetter (s[i - 1])
|
||||
&& CharacterFunctions::isUpperCase (s[i])
|
||||
&& ! CharacterFunctions::isUpperCase (s[i - 1]))
|
||||
s = s.substring (0, i) + " " + s.substring (i);
|
||||
|
||||
String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789");
|
||||
if (allowTemplates)
|
||||
allowedChars += "<>";
|
||||
|
||||
if (! removeColons)
|
||||
allowedChars += ":";
|
||||
|
||||
StringArray words;
|
||||
words.addTokens (s.retainCharacters (allowedChars), false);
|
||||
words.trim();
|
||||
|
||||
String n (words[0]);
|
||||
|
||||
if (capitalise)
|
||||
n = n.toLowerCase();
|
||||
|
||||
for (int i = 1; i < words.size(); ++i)
|
||||
{
|
||||
if (capitalise && words[i].length() > 1)
|
||||
n << words[i].substring (0, 1).toUpperCase()
|
||||
<< words[i].substring (1).toLowerCase();
|
||||
else
|
||||
n << words[i];
|
||||
}
|
||||
|
||||
if (CharacterFunctions::isDigit (n[0]))
|
||||
n = "_" + n;
|
||||
|
||||
if (CPlusPlusCodeTokeniser::isReservedKeyword (n))
|
||||
n << '_';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
String createIncludeStatement (const File& includeFile, const File& targetFile)
|
||||
{
|
||||
return createIncludeStatement (FileHelpers::unixStylePath (FileHelpers::getRelativePathFrom (includeFile, targetFile.getParentDirectory())));
|
||||
}
|
||||
|
||||
String createIncludeStatement (const String& includePath)
|
||||
{
|
||||
if (includePath.startsWithChar ('<') || includePath.startsWithChar ('"'))
|
||||
return "#include " + includePath;
|
||||
|
||||
return "#include \"" + includePath + "\"";
|
||||
}
|
||||
|
||||
String makeHeaderGuardName (const File& file)
|
||||
{
|
||||
return file.getFileName().toUpperCase()
|
||||
.replaceCharacters (" .", "__")
|
||||
.retainCharacters ("_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
+ "_INCLUDED";
|
||||
}
|
||||
|
||||
String makeBinaryDataIdentifierName (const File& file)
|
||||
{
|
||||
return makeValidIdentifier (file.getFileName()
|
||||
.replaceCharacters (" .", "__")
|
||||
.retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789"),
|
||||
false, true, false);
|
||||
}
|
||||
|
||||
String stringLiteral (const String& text, int maxLineLength)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return "String::empty";
|
||||
|
||||
StringArray lines;
|
||||
|
||||
{
|
||||
String::CharPointerType t (text.getCharPointer());
|
||||
bool finished = t.isEmpty();
|
||||
|
||||
while (! finished)
|
||||
{
|
||||
for (String::CharPointerType startOfLine (t);;)
|
||||
{
|
||||
switch (t.getAndAdvance())
|
||||
{
|
||||
case 0: finished = true; break;
|
||||
case '\n': break;
|
||||
case '\r': if (*t == '\n') ++t; break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
lines.add (String (startOfLine, t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLineLength > 0)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
String& line = lines.getReference (i);
|
||||
|
||||
if (line.length() > maxLineLength)
|
||||
{
|
||||
const String start (line.substring (0, maxLineLength));
|
||||
const String end (line.substring (maxLineLength));
|
||||
line = start;
|
||||
lines.insert (i + 1, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
lines.getReference(i) = CppTokeniserFunctions::addEscapeChars (lines.getReference(i));
|
||||
|
||||
lines.removeEmptyStrings();
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
lines.getReference(i) = "\"" + lines.getReference(i) + "\"";
|
||||
|
||||
String result (lines.joinIntoString (newLine));
|
||||
|
||||
if (! CharPointer_ASCII::isValidString (text.toUTF8(), std::numeric_limits<int>::max()))
|
||||
result = "CharPointer_UTF8 (" + result + ")";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String alignFunctionCallParams (const String& call, const StringArray& parameters, const int maxLineLength)
|
||||
{
|
||||
String result, currentLine (call);
|
||||
|
||||
for (int i = 0; i < parameters.size(); ++i)
|
||||
{
|
||||
if (currentLine.length() >= maxLineLength)
|
||||
{
|
||||
result += currentLine.trimEnd() + newLine;
|
||||
currentLine = String::repeatedString (" ", call.length()) + parameters[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLine += parameters[i];
|
||||
}
|
||||
|
||||
if (i < parameters.size() - 1)
|
||||
currentLine << ", ";
|
||||
}
|
||||
|
||||
return result + currentLine.trimEnd() + ")";
|
||||
}
|
||||
|
||||
String floatLiteral (double value, int numDecPlaces)
|
||||
{
|
||||
String s (value, numDecPlaces);
|
||||
|
||||
if (s.containsChar ('.'))
|
||||
s << 'f';
|
||||
else
|
||||
s << ".0f";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String boolLiteral (bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
String colourToCode (Colour col)
|
||||
{
|
||||
const Colour colours[] =
|
||||
{
|
||||
#define COL(col) Colours::col,
|
||||
#include "jucer_Colours.h"
|
||||
#undef COL
|
||||
Colours::transparentBlack
|
||||
};
|
||||
|
||||
static const char* colourNames[] =
|
||||
{
|
||||
#define COL(col) #col,
|
||||
#include "jucer_Colours.h"
|
||||
#undef COL
|
||||
0
|
||||
};
|
||||
|
||||
for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
|
||||
if (col == colours[i])
|
||||
return "Colours::" + String (colourNames[i]);
|
||||
|
||||
return "Colour (0x" + hexString8Digits ((int) col.getARGB()) + ')';
|
||||
}
|
||||
|
||||
String justificationToCode (Justification justification)
|
||||
{
|
||||
switch (justification.getFlags())
|
||||
{
|
||||
case Justification::centred: return "Justification::centred";
|
||||
case Justification::centredLeft: return "Justification::centredLeft";
|
||||
case Justification::centredRight: return "Justification::centredRight";
|
||||
case Justification::centredTop: return "Justification::centredTop";
|
||||
case Justification::centredBottom: return "Justification::centredBottom";
|
||||
case Justification::topLeft: return "Justification::topLeft";
|
||||
case Justification::topRight: return "Justification::topRight";
|
||||
case Justification::bottomLeft: return "Justification::bottomLeft";
|
||||
case Justification::bottomRight: return "Justification::bottomRight";
|
||||
case Justification::left: return "Justification::left";
|
||||
case Justification::right: return "Justification::right";
|
||||
case Justification::horizontallyCentred: return "Justification::horizontallyCentred";
|
||||
case Justification::top: return "Justification::top";
|
||||
case Justification::bottom: return "Justification::bottom";
|
||||
case Justification::verticallyCentred: return "Justification::verticallyCentred";
|
||||
case Justification::horizontallyJustified: return "Justification::horizontallyJustified";
|
||||
default: break;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return "Justification (" + String (justification.getFlags()) + ")";
|
||||
}
|
||||
|
||||
void writeDataAsCppLiteral (const MemoryBlock& mb, OutputStream& out,
|
||||
bool breakAtNewLines, bool allowStringBreaks)
|
||||
{
|
||||
const int maxCharsOnLine = 250;
|
||||
|
||||
const unsigned char* data = (const unsigned char*) mb.getData();
|
||||
int charsOnLine = 0;
|
||||
|
||||
bool canUseStringLiteral = mb.getSize() < 32768; // MS compilers can't handle big string literals..
|
||||
|
||||
if (canUseStringLiteral)
|
||||
{
|
||||
unsigned int numEscaped = 0;
|
||||
|
||||
for (size_t i = 0; i < mb.getSize(); ++i)
|
||||
{
|
||||
const unsigned int num = (unsigned int) data[i];
|
||||
if (! ((num >= 32 && num < 127) || num == '\t' || num == '\r' || num == '\n'))
|
||||
{
|
||||
if (++numEscaped > mb.getSize() / 4)
|
||||
{
|
||||
canUseStringLiteral = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! canUseStringLiteral)
|
||||
{
|
||||
out << "{ ";
|
||||
|
||||
for (size_t i = 0; i < mb.getSize(); ++i)
|
||||
{
|
||||
const int num = (int) (unsigned int) data[i];
|
||||
out << num << ',';
|
||||
|
||||
charsOnLine += 2;
|
||||
|
||||
if (num >= 10)
|
||||
{
|
||||
++charsOnLine;
|
||||
|
||||
if (num >= 100)
|
||||
++charsOnLine;
|
||||
}
|
||||
|
||||
if (charsOnLine >= maxCharsOnLine)
|
||||
{
|
||||
charsOnLine = 0;
|
||||
out << newLine;
|
||||
}
|
||||
}
|
||||
|
||||
out << "0,0 };";
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "\"";
|
||||
CppTokeniserFunctions::writeEscapeChars (out, (const char*) data, (int) mb.getSize(),
|
||||
maxCharsOnLine, breakAtNewLines, false, allowStringBreaks);
|
||||
out << "\";";
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static unsigned int calculateHash (const String& s, const unsigned int hashMultiplier)
|
||||
{
|
||||
const char* t = s.toUTF8();
|
||||
unsigned int hash = 0;
|
||||
while (*t != 0)
|
||||
hash = hashMultiplier * hash + (unsigned int) *t++;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static unsigned int findBestHashMultiplier (const StringArray& strings)
|
||||
{
|
||||
unsigned int v = 31;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SortedSet <unsigned int> hashes;
|
||||
bool collision = false;
|
||||
for (int i = strings.size(); --i >= 0;)
|
||||
{
|
||||
const unsigned int hash = calculateHash (strings[i], v);
|
||||
if (hashes.contains (hash))
|
||||
{
|
||||
collision = true;
|
||||
break;
|
||||
}
|
||||
|
||||
hashes.add (hash);
|
||||
}
|
||||
|
||||
if (! collision)
|
||||
break;
|
||||
|
||||
v += 2;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void createStringMatcher (OutputStream& out, const String& utf8PointerVariable,
|
||||
const StringArray& strings, const StringArray& codeToExecute, const int indentLevel)
|
||||
{
|
||||
jassert (strings.size() == codeToExecute.size());
|
||||
const String indent (String::repeatedString (" ", indentLevel));
|
||||
const unsigned int hashMultiplier = findBestHashMultiplier (strings);
|
||||
|
||||
out << indent << "unsigned int hash = 0;" << newLine
|
||||
<< indent << "if (" << utf8PointerVariable << " != 0)" << newLine
|
||||
<< indent << " while (*" << utf8PointerVariable << " != 0)" << newLine
|
||||
<< indent << " hash = " << (int) hashMultiplier << " * hash + (unsigned int) *" << utf8PointerVariable << "++;" << newLine
|
||||
<< newLine
|
||||
<< indent << "switch (hash)" << newLine
|
||||
<< indent << "{" << newLine;
|
||||
|
||||
for (int i = 0; i < strings.size(); ++i)
|
||||
{
|
||||
out << indent << " case 0x" << hexString8Digits ((int) calculateHash (strings[i], hashMultiplier))
|
||||
<< ": " << codeToExecute[i] << newLine;
|
||||
}
|
||||
|
||||
out << indent << " default: break;" << newLine
|
||||
<< indent << "}" << newLine << newLine;
|
||||
}
|
||||
|
||||
String getLeadingWhitespace (String line)
|
||||
{
|
||||
line = line.removeCharacters ("\r\n");
|
||||
const String::CharPointerType endOfLeadingWS (line.getCharPointer().findEndOfWhitespace());
|
||||
return String (line.getCharPointer(), endOfLeadingWS);
|
||||
}
|
||||
|
||||
int getBraceCount (String::CharPointerType line)
|
||||
{
|
||||
int braces = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const juce_wchar c = line.getAndAdvance();
|
||||
|
||||
if (c == 0) break;
|
||||
else if (c == '{') ++braces;
|
||||
else if (c == '}') --braces;
|
||||
else if (c == '/') { if (*line == '/') break; }
|
||||
else if (c == '"' || c == '\'') { while (! (line.isEmpty() || line.getAndAdvance() == c)) {} }
|
||||
}
|
||||
|
||||
return braces;
|
||||
}
|
||||
|
||||
bool getIndentForCurrentBlock (CodeDocument::Position pos, const String& tab,
|
||||
String& blockIndent, String& lastLineIndent)
|
||||
{
|
||||
int braceCount = 0;
|
||||
bool indentFound = false;
|
||||
|
||||
while (pos.getLineNumber() > 0)
|
||||
{
|
||||
pos = pos.movedByLines (-1);
|
||||
|
||||
const String line (pos.getLineText());
|
||||
const String trimmedLine (line.trimStart());
|
||||
|
||||
braceCount += getBraceCount (trimmedLine.getCharPointer());
|
||||
|
||||
if (braceCount > 0)
|
||||
{
|
||||
blockIndent = getLeadingWhitespace (line);
|
||||
if (! indentFound)
|
||||
lastLineIndent = blockIndent + tab;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((! indentFound) && trimmedLine.isNotEmpty())
|
||||
{
|
||||
indentFound = true;
|
||||
lastLineIndent = getLeadingWhitespace (line);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_CODEHELPERS_JUCEHEADER__
|
||||
#define __JUCER_CODEHELPERS_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace CodeHelpers
|
||||
{
|
||||
String indent (const String& code, const int numSpaces, bool indentFirstLine);
|
||||
String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates);
|
||||
String createIncludeStatement (const File& includedFile, const File& targetFile);
|
||||
String createIncludeStatement (const String& includePath);
|
||||
String makeHeaderGuardName (const File& file);
|
||||
String makeBinaryDataIdentifierName (const File& file);
|
||||
|
||||
String stringLiteral (const String& text, int maxLineLength = -1);
|
||||
String floatLiteral (double value, int numDecPlaces);
|
||||
String boolLiteral (bool value);
|
||||
|
||||
String colourToCode (Colour col);
|
||||
String justificationToCode (Justification);
|
||||
|
||||
String alignFunctionCallParams (const String& call, const StringArray& parameters, int maxLineLength);
|
||||
|
||||
void writeDataAsCppLiteral (const MemoryBlock& data, OutputStream& out,
|
||||
bool breakAtNewLines, bool allowStringBreaks);
|
||||
|
||||
void createStringMatcher (OutputStream& out, const String& utf8PointerVariable,
|
||||
const StringArray& strings, const StringArray& codeToExecute, const int indentLevel);
|
||||
|
||||
String getLeadingWhitespace (String line);
|
||||
int getBraceCount (String::CharPointerType line);
|
||||
bool getIndentForCurrentBlock (CodeDocument::Position pos, const String& tab,
|
||||
String& blockIndent, String& lastLineIndent);
|
||||
}
|
||||
|
||||
|
||||
#endif // __JUCER_CODEHELPERS_JUCEHEADER__
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
COL(black)
|
||||
COL(white)
|
||||
COL(blue)
|
||||
COL(grey)
|
||||
COL(green)
|
||||
COL(red)
|
||||
COL(yellow)
|
||||
COL(aliceblue)
|
||||
COL(antiquewhite)
|
||||
COL(aqua)
|
||||
COL(aquamarine)
|
||||
COL(azure)
|
||||
COL(beige)
|
||||
COL(bisque)
|
||||
COL(blanchedalmond)
|
||||
COL(blueviolet)
|
||||
COL(brown)
|
||||
COL(burlywood)
|
||||
COL(cadetblue)
|
||||
COL(chartreuse)
|
||||
COL(chocolate)
|
||||
COL(coral)
|
||||
COL(cornflowerblue)
|
||||
COL(cornsilk)
|
||||
COL(crimson)
|
||||
COL(cyan)
|
||||
COL(darkblue)
|
||||
COL(darkcyan)
|
||||
COL(darkgoldenrod)
|
||||
COL(darkgrey)
|
||||
COL(darkgreen)
|
||||
COL(darkkhaki)
|
||||
COL(darkmagenta)
|
||||
COL(darkolivegreen)
|
||||
COL(darkorange)
|
||||
COL(darkorchid)
|
||||
COL(darkred)
|
||||
COL(darksalmon)
|
||||
COL(darkseagreen)
|
||||
COL(darkslateblue)
|
||||
COL(darkslategrey)
|
||||
COL(darkturquoise)
|
||||
COL(darkviolet)
|
||||
COL(deeppink)
|
||||
COL(deepskyblue)
|
||||
COL(dimgrey)
|
||||
COL(dodgerblue)
|
||||
COL(firebrick)
|
||||
COL(floralwhite)
|
||||
COL(forestgreen)
|
||||
COL(fuchsia)
|
||||
COL(gainsboro)
|
||||
COL(gold)
|
||||
COL(goldenrod)
|
||||
COL(greenyellow)
|
||||
COL(honeydew)
|
||||
COL(hotpink)
|
||||
COL(indianred)
|
||||
COL(indigo)
|
||||
COL(ivory)
|
||||
COL(khaki)
|
||||
COL(lavender)
|
||||
COL(lavenderblush)
|
||||
COL(lemonchiffon)
|
||||
COL(lightblue)
|
||||
COL(lightcoral)
|
||||
COL(lightcyan)
|
||||
COL(lightgoldenrodyellow)
|
||||
COL(lightgreen)
|
||||
COL(lightgrey)
|
||||
COL(lightpink)
|
||||
COL(lightsalmon)
|
||||
COL(lightseagreen)
|
||||
COL(lightskyblue)
|
||||
COL(lightslategrey)
|
||||
COL(lightsteelblue)
|
||||
COL(lightyellow)
|
||||
COL(lime)
|
||||
COL(limegreen)
|
||||
COL(linen)
|
||||
COL(magenta)
|
||||
COL(maroon)
|
||||
COL(mediumaquamarine)
|
||||
COL(mediumblue)
|
||||
COL(mediumorchid)
|
||||
COL(mediumpurple)
|
||||
COL(mediumseagreen)
|
||||
COL(mediumslateblue)
|
||||
COL(mediumspringgreen)
|
||||
COL(mediumturquoise)
|
||||
COL(mediumvioletred)
|
||||
COL(midnightblue)
|
||||
COL(mintcream)
|
||||
COL(mistyrose)
|
||||
COL(navajowhite)
|
||||
COL(navy)
|
||||
COL(oldlace)
|
||||
COL(olive)
|
||||
COL(olivedrab)
|
||||
COL(orange)
|
||||
COL(orangered)
|
||||
COL(orchid)
|
||||
COL(palegoldenrod)
|
||||
COL(palegreen)
|
||||
COL(paleturquoise)
|
||||
COL(palevioletred)
|
||||
COL(papayawhip)
|
||||
COL(peachpuff)
|
||||
COL(peru)
|
||||
COL(pink)
|
||||
COL(plum)
|
||||
COL(powderblue)
|
||||
COL(purple)
|
||||
COL(rosybrown)
|
||||
COL(royalblue)
|
||||
COL(saddlebrown)
|
||||
COL(salmon)
|
||||
COL(sandybrown)
|
||||
COL(seagreen)
|
||||
COL(seashell)
|
||||
COL(sienna)
|
||||
COL(silver)
|
||||
COL(skyblue)
|
||||
COL(slateblue)
|
||||
COL(slategrey)
|
||||
COL(snow)
|
||||
COL(springgreen)
|
||||
COL(steelblue)
|
||||
COL(tan)
|
||||
COL(teal)
|
||||
COL(thistle)
|
||||
COL(tomato)
|
||||
COL(turquoise)
|
||||
COL(violet)
|
||||
COL(wheat)
|
||||
COL(whitesmoke)
|
||||
COL(yellowgreen)
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "jucer_CodeHelpers.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace FileHelpers
|
||||
{
|
||||
static int64 calculateMemoryHashCode (const void* data, const size_t numBytes)
|
||||
{
|
||||
int64 t = 0;
|
||||
|
||||
for (size_t i = 0; i < numBytes; ++i)
|
||||
t = t * 65599 + static_cast <const uint8*> (data)[i];
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int64 calculateStreamHashCode (InputStream& in)
|
||||
{
|
||||
int64 t = 0;
|
||||
|
||||
const int bufferSize = 4096;
|
||||
HeapBlock <uint8> buffer;
|
||||
buffer.malloc (bufferSize);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const int num = in.read (buffer, bufferSize);
|
||||
|
||||
if (num <= 0)
|
||||
break;
|
||||
|
||||
for (int i = 0; i < num; ++i)
|
||||
t = t * 65599 + buffer[i];
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int64 calculateFileHashCode (const File& file)
|
||||
{
|
||||
ScopedPointer <FileInputStream> stream (file.createInputStream());
|
||||
return stream != nullptr ? calculateStreamHashCode (*stream) : 0;
|
||||
}
|
||||
|
||||
bool overwriteFileWithNewDataIfDifferent (const File& file, const void* data, size_t numBytes)
|
||||
{
|
||||
if (file.getSize() == (int64) numBytes
|
||||
&& calculateMemoryHashCode (data, numBytes) == calculateFileHashCode (file))
|
||||
return true;
|
||||
|
||||
if (file.exists())
|
||||
return file.replaceWithData (data, numBytes);
|
||||
|
||||
return file.getParentDirectory().createDirectory() && file.appendData (data, numBytes);
|
||||
}
|
||||
|
||||
bool overwriteFileWithNewDataIfDifferent (const File& file, const MemoryOutputStream& newData)
|
||||
{
|
||||
return overwriteFileWithNewDataIfDifferent (file, newData.getData(), newData.getDataSize());
|
||||
}
|
||||
|
||||
bool overwriteFileWithNewDataIfDifferent (const File& file, const String& newData)
|
||||
{
|
||||
const char* const utf8 = newData.toUTF8();
|
||||
return overwriteFileWithNewDataIfDifferent (file, utf8, strlen (utf8));
|
||||
}
|
||||
|
||||
bool containsAnyNonHiddenFiles (const File& folder)
|
||||
{
|
||||
DirectoryIterator di (folder, false);
|
||||
|
||||
while (di.next())
|
||||
if (! di.getFile().isHidden())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String unixStylePath (const String& path) { return path.replaceCharacter ('\\', '/'); }
|
||||
String windowsStylePath (const String& path) { return path.replaceCharacter ('/', '\\'); }
|
||||
|
||||
String currentOSStylePath (const String& path)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
return windowsStylePath (path);
|
||||
#else
|
||||
return unixStylePath (path);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isAbsolutePath (const String& path)
|
||||
{
|
||||
return File::isAbsolutePath (path)
|
||||
|| path.startsWithChar ('/') // (needed because File::isAbsolutePath will ignore forward-slashes on Windows)
|
||||
|| path.startsWithChar ('$')
|
||||
|| path.startsWithChar ('~')
|
||||
|| (CharacterFunctions::isLetter (path[0]) && path[1] == ':')
|
||||
|| path.startsWithIgnoreCase ("smb:");
|
||||
}
|
||||
|
||||
String appendPath (const String& path, const String& subpath)
|
||||
{
|
||||
if (isAbsolutePath (subpath))
|
||||
return unixStylePath (subpath);
|
||||
|
||||
String path1 (unixStylePath (path));
|
||||
if (! path1.endsWithChar ('/'))
|
||||
path1 << '/';
|
||||
|
||||
return path1 + unixStylePath (subpath);
|
||||
}
|
||||
|
||||
bool shouldPathsBeRelative (String path1, String path2)
|
||||
{
|
||||
path1 = unixStylePath (path1);
|
||||
path2 = unixStylePath (path2);
|
||||
|
||||
const int len = jmin (path1.length(), path2.length());
|
||||
int commonBitLength = 0;
|
||||
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
if (CharacterFunctions::toLowerCase (path1[i]) != CharacterFunctions::toLowerCase (path2[i]))
|
||||
break;
|
||||
|
||||
++commonBitLength;
|
||||
}
|
||||
|
||||
return path1.substring (0, commonBitLength).removeCharacters ("/:").isNotEmpty();
|
||||
}
|
||||
|
||||
String getRelativePathFrom (const File& file, const File& sourceFolder)
|
||||
{
|
||||
#if ! JUCE_WINDOWS
|
||||
// On a non-windows machine, we can't know if a drive-letter path may be relative or not.
|
||||
if (CharacterFunctions::isLetter (file.getFullPathName()[0]) && file.getFullPathName()[1] == ':')
|
||||
return file.getFullPathName();
|
||||
#endif
|
||||
|
||||
return file.getRelativePathFrom (sourceFolder);
|
||||
}
|
||||
|
||||
// removes "/../" bits from the middle of the path
|
||||
String simplifyPath (String::CharPointerType p)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
if (CharacterFunctions::indexOf (p, CharPointer_ASCII ("/../")) >= 0
|
||||
|| CharacterFunctions::indexOf (p, CharPointer_ASCII ("\\..\\")) >= 0)
|
||||
#else
|
||||
if (CharacterFunctions::indexOf (p, CharPointer_ASCII ("/../")) >= 0)
|
||||
#endif
|
||||
{
|
||||
StringArray toks;
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
toks.addTokens (p, "\\/", StringRef());
|
||||
#else
|
||||
toks.addTokens (p, "/", StringRef());
|
||||
#endif
|
||||
|
||||
while (toks[0] == ".")
|
||||
toks.remove (0);
|
||||
|
||||
for (int i = 1; i < toks.size(); ++i)
|
||||
{
|
||||
if (toks[i] == ".." && toks [i - 1] != "..")
|
||||
{
|
||||
toks.removeRange (i - 1, 2);
|
||||
i = jmax (0, i - 2);
|
||||
}
|
||||
}
|
||||
|
||||
return toks.joinIntoString ("/");
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
String simplifyPath (const String& path)
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
if (path.contains ("\\..\\") || path.contains ("/../"))
|
||||
#else
|
||||
if (path.contains ("/../"))
|
||||
#endif
|
||||
return simplifyPath (path.getCharPointer());
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_FILEHELPERS_JUCEHEADER__
|
||||
#define __JUCER_FILEHELPERS_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace FileHelpers
|
||||
{
|
||||
int64 calculateStreamHashCode (InputStream& stream);
|
||||
int64 calculateFileHashCode (const File& file);
|
||||
|
||||
bool overwriteFileWithNewDataIfDifferent (const File& file, const void* data, size_t numBytes);
|
||||
bool overwriteFileWithNewDataIfDifferent (const File& file, const MemoryOutputStream& newData);
|
||||
bool overwriteFileWithNewDataIfDifferent (const File& file, const String& newData);
|
||||
|
||||
bool containsAnyNonHiddenFiles (const File& folder);
|
||||
|
||||
String unixStylePath (const String& path);
|
||||
String windowsStylePath (const String& path);
|
||||
String currentOSStylePath (const String& path);
|
||||
|
||||
bool shouldPathsBeRelative (String path1, String path2);
|
||||
bool isAbsolutePath (const String& path);
|
||||
|
||||
// A windows-aware version of File::getRelativePath()
|
||||
String getRelativePathFrom (const File& file, const File& sourceFolder);
|
||||
|
||||
// removes "/../" bits from the middle of the path
|
||||
String simplifyPath (String::CharPointerType path);
|
||||
String simplifyPath (const String& path);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class FileModificationDetector
|
||||
{
|
||||
public:
|
||||
FileModificationDetector (const File& f)
|
||||
: file (f)
|
||||
{
|
||||
}
|
||||
|
||||
const File& getFile() const { return file; }
|
||||
void fileHasBeenRenamed (const File& newFile) { file = newFile; }
|
||||
|
||||
bool hasBeenModified() const
|
||||
{
|
||||
return fileModificationTime != file.getLastModificationTime()
|
||||
&& (fileSize != file.getSize()
|
||||
|| FileHelpers::calculateFileHashCode (file) != fileHashCode);
|
||||
}
|
||||
|
||||
void updateHash()
|
||||
{
|
||||
fileModificationTime = file.getLastModificationTime();
|
||||
fileSize = file.getSize();
|
||||
fileHashCode = FileHelpers::calculateFileHashCode (file);
|
||||
}
|
||||
|
||||
private:
|
||||
File file;
|
||||
Time fileModificationTime;
|
||||
int64 fileHashCode, fileSize;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_FILEHELPERS_JUCEHEADER__
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_ICONS_JUCEHEADER__
|
||||
#define __JUCER_ICONS_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct Icon
|
||||
{
|
||||
Icon() : path (nullptr) {}
|
||||
Icon (const Path& p, Colour c) : path (&p), colour (c) {}
|
||||
Icon (const Path* p, Colour c) : path (p), colour (c) {}
|
||||
|
||||
void draw (Graphics& g, const Rectangle<float>& area, bool isCrossedOut) const
|
||||
{
|
||||
if (path != nullptr)
|
||||
{
|
||||
g.setColour (colour);
|
||||
|
||||
const RectanglePlacement placement (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize);
|
||||
g.fillPath (*path, placement.getTransformToFit (path->getBounds(), area));
|
||||
|
||||
if (isCrossedOut)
|
||||
{
|
||||
g.setColour (Colours::red.withAlpha (0.8f));
|
||||
g.drawLine ((float) area.getX(), area.getY() + area.getHeight() * 0.2f,
|
||||
(float) area.getRight(), area.getY() + area.getHeight() * 0.8f, 3.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Icon withContrastingColourTo (Colour background) const
|
||||
{
|
||||
return Icon (path, background.contrasting (colour, 0.6f));
|
||||
}
|
||||
|
||||
const Path* path;
|
||||
Colour colour;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class Icons
|
||||
{
|
||||
public:
|
||||
Icons();
|
||||
|
||||
Path folder, document, imageDoc,
|
||||
config, exporter, juceLogo,
|
||||
graph, jigsaw, info, warning,
|
||||
bug, mainJuceLogo;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Icons)
|
||||
};
|
||||
|
||||
const Icons& getIcons();
|
||||
|
||||
|
||||
#endif // __JUCER_ICONS_JUCEHEADER__
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_JucerTreeViewBase.h"
|
||||
#include "../Project/jucer_ProjectContentComponent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void TreePanelBase::setRoot (JucerTreeViewBase* root)
|
||||
{
|
||||
rootItem = root;
|
||||
tree.setRootItem (root);
|
||||
tree.getRootItem()->setOpen (true);
|
||||
|
||||
if (project != nullptr)
|
||||
{
|
||||
const ScopedPointer<XmlElement> treeOpenness (project->getStoredProperties()
|
||||
.getXmlValue (opennessStateKey));
|
||||
if (treeOpenness != nullptr)
|
||||
{
|
||||
tree.restoreOpennessState (*treeOpenness, true);
|
||||
|
||||
for (int i = tree.getNumSelectedItems(); --i >= 0;)
|
||||
if (JucerTreeViewBase* item = dynamic_cast<JucerTreeViewBase*> (tree.getSelectedItem (i)))
|
||||
item->cancelDelayedSelectionTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreePanelBase::saveOpenness()
|
||||
{
|
||||
if (project != nullptr)
|
||||
{
|
||||
const ScopedPointer<XmlElement> opennessState (tree.getOpennessState (true));
|
||||
|
||||
if (opennessState != nullptr)
|
||||
project->getStoredProperties().setValue (opennessStateKey, opennessState);
|
||||
else
|
||||
project->getStoredProperties().removeValue (opennessStateKey);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JucerTreeViewBase::JucerTreeViewBase() : textX (0)
|
||||
{
|
||||
setLinesDrawnForSubItems (false);
|
||||
}
|
||||
|
||||
JucerTreeViewBase::~JucerTreeViewBase()
|
||||
{
|
||||
masterReference.clear();
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::refreshSubItems()
|
||||
{
|
||||
WholeTreeOpennessRestorer wtor (*this);
|
||||
clearSubItems();
|
||||
addSubItems();
|
||||
}
|
||||
|
||||
Font JucerTreeViewBase::getFont() const
|
||||
{
|
||||
return Font (getItemHeight() * 0.6f);
|
||||
}
|
||||
|
||||
float JucerTreeViewBase::getIconSize() const
|
||||
{
|
||||
return jmin (getItemHeight() - 4.0f, 18.0f);
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour /*backgroundColour*/, bool isMouseOver)
|
||||
{
|
||||
TreeViewItem::paintOpenCloseButton (g, area, getOwnerView()->findColour (mainBackgroundColourId), isMouseOver);
|
||||
}
|
||||
|
||||
Colour JucerTreeViewBase::getBackgroundColour() const
|
||||
{
|
||||
Colour background (getOwnerView()->findColour (mainBackgroundColourId));
|
||||
|
||||
if (isSelected())
|
||||
background = background.overlaidWith (getOwnerView()->findColour (TreeView::selectedItemBackgroundColourId));
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
Colour JucerTreeViewBase::getContrastingColour (float contrast) const
|
||||
{
|
||||
return getBackgroundColour().contrasting (contrast);
|
||||
}
|
||||
|
||||
Colour JucerTreeViewBase::getContrastingColour (Colour target, float minContrast) const
|
||||
{
|
||||
return getBackgroundColour().contrasting (target, minContrast);
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::paintContent (Graphics& g, const Rectangle<int>& area)
|
||||
{
|
||||
g.setFont (getFont());
|
||||
g.setColour (isMissing() ? getContrastingColour (Colours::red, 0.8f)
|
||||
: getContrastingColour (0.8f));
|
||||
|
||||
g.drawFittedText (getDisplayName(), area, Justification::centredLeft, 1, 0.8f);
|
||||
}
|
||||
|
||||
Component* JucerTreeViewBase::createItemComponent()
|
||||
{
|
||||
return new TreeItemComponent (*this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class RenameTreeItemCallback : public ModalComponentManager::Callback,
|
||||
public TextEditorListener
|
||||
{
|
||||
public:
|
||||
RenameTreeItemCallback (JucerTreeViewBase& ti, Component& parent, const Rectangle<int>& bounds)
|
||||
: item (ti)
|
||||
{
|
||||
ed.setMultiLine (false, false);
|
||||
ed.setPopupMenuEnabled (false);
|
||||
ed.setSelectAllWhenFocused (true);
|
||||
ed.setFont (item.getFont());
|
||||
ed.addListener (this);
|
||||
ed.setText (item.getRenamingName());
|
||||
ed.setBounds (bounds);
|
||||
|
||||
parent.addAndMakeVisible (ed);
|
||||
ed.enterModalState (true, this);
|
||||
}
|
||||
|
||||
void modalStateFinished (int resultCode) override
|
||||
{
|
||||
if (resultCode != 0)
|
||||
item.setName (ed.getText());
|
||||
}
|
||||
|
||||
void textEditorTextChanged (TextEditor&) override {}
|
||||
void textEditorReturnKeyPressed (TextEditor& editor) override { editor.exitModalState (1); }
|
||||
void textEditorEscapeKeyPressed (TextEditor& editor) override { editor.exitModalState (0); }
|
||||
void textEditorFocusLost (TextEditor& editor) override { editor.exitModalState (0); }
|
||||
|
||||
private:
|
||||
struct RenameEditor : public TextEditor
|
||||
{
|
||||
void inputAttemptWhenModal() override { exitModalState (0); }
|
||||
};
|
||||
|
||||
RenameEditor ed;
|
||||
JucerTreeViewBase& item;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (RenameTreeItemCallback)
|
||||
};
|
||||
|
||||
void JucerTreeViewBase::showRenameBox()
|
||||
{
|
||||
Rectangle<int> r (getItemPosition (true));
|
||||
r.setLeft (r.getX() + textX);
|
||||
r.setHeight (getItemHeight());
|
||||
|
||||
new RenameTreeItemCallback (*this, *getOwnerView(), r);
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::itemClicked (const MouseEvent& e)
|
||||
{
|
||||
if (e.mods.isPopupMenu())
|
||||
{
|
||||
if (getOwnerView()->getNumSelectedItems() > 1)
|
||||
showMultiSelectionPopupMenu();
|
||||
else
|
||||
showPopupMenu();
|
||||
}
|
||||
else if (isSelected())
|
||||
{
|
||||
itemSelectionChanged (true);
|
||||
}
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::deleteItem() {}
|
||||
void JucerTreeViewBase::deleteAllSelectedItems() {}
|
||||
void JucerTreeViewBase::showDocument() {}
|
||||
void JucerTreeViewBase::showPopupMenu() {}
|
||||
void JucerTreeViewBase::showMultiSelectionPopupMenu() {}
|
||||
|
||||
static void treeViewMenuItemChosen (int resultCode, WeakReference<JucerTreeViewBase> item)
|
||||
{
|
||||
if (item != nullptr)
|
||||
item->handlePopupMenuResult (resultCode);
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::launchPopupMenu (PopupMenu& m)
|
||||
{
|
||||
m.showMenuAsync (PopupMenu::Options(),
|
||||
ModalCallbackFunction::create (treeViewMenuItemChosen, WeakReference<JucerTreeViewBase> (this)));
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::handlePopupMenuResult (int)
|
||||
{
|
||||
}
|
||||
|
||||
ProjectContentComponent* JucerTreeViewBase::getProjectContentComponent() const
|
||||
{
|
||||
for (Component* c = getOwnerView(); c != nullptr; c = c->getParentComponent())
|
||||
if (ProjectContentComponent* pcc = dynamic_cast <ProjectContentComponent*> (c))
|
||||
return pcc;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JucerTreeViewBase::ItemSelectionTimer : public Timer
|
||||
{
|
||||
public:
|
||||
ItemSelectionTimer (JucerTreeViewBase& tvb) : owner (tvb) {}
|
||||
|
||||
void timerCallback() override { owner.invokeShowDocument(); }
|
||||
|
||||
private:
|
||||
JucerTreeViewBase& owner;
|
||||
JUCE_DECLARE_NON_COPYABLE (ItemSelectionTimer)
|
||||
};
|
||||
|
||||
void JucerTreeViewBase::itemSelectionChanged (bool isNowSelected)
|
||||
{
|
||||
if (isNowSelected)
|
||||
{
|
||||
delayedSelectionTimer = new ItemSelectionTimer (*this);
|
||||
delayedSelectionTimer->startTimer (getMillisecsAllowedForDragGesture());
|
||||
}
|
||||
else
|
||||
{
|
||||
cancelDelayedSelectionTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::invokeShowDocument()
|
||||
{
|
||||
cancelDelayedSelectionTimer();
|
||||
showDocument();
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::itemDoubleClicked (const MouseEvent&)
|
||||
{
|
||||
invokeShowDocument();
|
||||
}
|
||||
|
||||
void JucerTreeViewBase::cancelDelayedSelectionTimer()
|
||||
{
|
||||
delayedSelectionTimer = nullptr;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_JUCERTREEVIEWBASE_JUCEHEADER__
|
||||
#define __JUCER_JUCERTREEVIEWBASE_JUCEHEADER__
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
class ProjectContentComponent;
|
||||
class Project;
|
||||
|
||||
//==============================================================================
|
||||
class JucerTreeViewBase : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
JucerTreeViewBase();
|
||||
~JucerTreeViewBase();
|
||||
|
||||
int getItemWidth() const override { return -1; }
|
||||
int getItemHeight() const override { return 20; }
|
||||
|
||||
void paintOpenCloseButton (Graphics&, const Rectangle<float>& area, Colour backgroundColour, bool isMouseOver) override;
|
||||
Component* createItemComponent() override;
|
||||
void itemClicked (const MouseEvent& e) override;
|
||||
void itemSelectionChanged (bool isNowSelected) override;
|
||||
void itemDoubleClicked (const MouseEvent&) override;
|
||||
|
||||
void cancelDelayedSelectionTimer();
|
||||
|
||||
//==============================================================================
|
||||
virtual Font getFont() const;
|
||||
virtual String getRenamingName() const = 0;
|
||||
virtual String getDisplayName() const = 0;
|
||||
virtual void setName (const String& newName) = 0;
|
||||
virtual bool isMissing() = 0;
|
||||
virtual Icon getIcon() const = 0;
|
||||
virtual float getIconSize() const;
|
||||
virtual bool isIconCrossedOut() const { return false; }
|
||||
virtual void paintContent (Graphics& g, const Rectangle<int>& area);
|
||||
virtual int getMillisecsAllowedForDragGesture() { return 120; };
|
||||
virtual File getDraggableFile() const { return File::nonexistent; }
|
||||
|
||||
void refreshSubItems();
|
||||
virtual void deleteItem();
|
||||
virtual void deleteAllSelectedItems();
|
||||
virtual void showDocument();
|
||||
virtual void showMultiSelectionPopupMenu();
|
||||
virtual void showRenameBox();
|
||||
|
||||
void launchPopupMenu (PopupMenu&); // runs asynchronously, and produces a callback to handlePopupMenuResult().
|
||||
virtual void showPopupMenu();
|
||||
virtual void handlePopupMenuResult (int resultCode);
|
||||
|
||||
//==============================================================================
|
||||
// To handle situations where an item gets deleted before openness is
|
||||
// restored for it, this OpennessRestorer keeps only a pointer to the
|
||||
// topmost tree item.
|
||||
struct WholeTreeOpennessRestorer : public OpennessRestorer
|
||||
{
|
||||
WholeTreeOpennessRestorer (TreeViewItem& item) : OpennessRestorer (getTopLevelItem (item))
|
||||
{}
|
||||
|
||||
private:
|
||||
static TreeViewItem& getTopLevelItem (TreeViewItem& item)
|
||||
{
|
||||
if (TreeViewItem* const p = item.getParentItem())
|
||||
return getTopLevelItem (*p);
|
||||
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
int textX;
|
||||
|
||||
protected:
|
||||
ProjectContentComponent* getProjectContentComponent() const;
|
||||
virtual void addSubItems() {}
|
||||
|
||||
Colour getBackgroundColour() const;
|
||||
Colour getContrastingColour (float contrast) const;
|
||||
Colour getContrastingColour (Colour targetColour, float minContrast) const;
|
||||
|
||||
private:
|
||||
class ItemSelectionTimer;
|
||||
friend class ItemSelectionTimer;
|
||||
ScopedPointer<Timer> delayedSelectionTimer;
|
||||
|
||||
WeakReference<JucerTreeViewBase>::Master masterReference;
|
||||
friend class WeakReference<JucerTreeViewBase>;
|
||||
|
||||
void invokeShowDocument();
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TreePanelBase : public Component
|
||||
{
|
||||
public:
|
||||
TreePanelBase (const Project* p, const String& treeviewID)
|
||||
: project (p), opennessStateKey (treeviewID)
|
||||
{
|
||||
addAndMakeVisible (tree);
|
||||
tree.setRootItemVisible (true);
|
||||
tree.setDefaultOpenness (true);
|
||||
tree.setColour (TreeView::backgroundColourId, Colours::transparentBlack);
|
||||
tree.setIndentSize (14);
|
||||
tree.getViewport()->setScrollBarThickness (14);
|
||||
}
|
||||
|
||||
~TreePanelBase()
|
||||
{
|
||||
tree.setRootItem (nullptr);
|
||||
}
|
||||
|
||||
void setRoot (JucerTreeViewBase* root);
|
||||
void saveOpenness();
|
||||
|
||||
void deleteSelectedItems()
|
||||
{
|
||||
if (rootItem != nullptr)
|
||||
rootItem->deleteAllSelectedItems();
|
||||
}
|
||||
|
||||
void setEmptyTreeMessage (const String& newMessage)
|
||||
{
|
||||
if (emptyTreeMessage != newMessage)
|
||||
{
|
||||
emptyTreeMessage = newMessage;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
static void drawEmptyPanelMessage (Component& comp, Graphics& g, const String& message)
|
||||
{
|
||||
const int fontHeight = 13;
|
||||
const Rectangle<int> area (comp.getLocalBounds());
|
||||
g.setColour (comp.findColour (mainBackgroundColourId).contrasting (0.7f));
|
||||
g.setFont ((float) fontHeight);
|
||||
g.drawFittedText (message, area.reduced (4, 2), Justification::centred, area.getHeight() / fontHeight);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (emptyTreeMessage.isNotEmpty() && (rootItem == nullptr || rootItem->getNumSubItems() == 0))
|
||||
drawEmptyPanelMessage (*this, g, emptyTreeMessage);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
tree.setBounds (getAvailableBounds());
|
||||
}
|
||||
|
||||
Rectangle<int> getAvailableBounds() const
|
||||
{
|
||||
return Rectangle<int> (0, 2, getWidth() - 2, getHeight() - 2);
|
||||
}
|
||||
|
||||
const Project* project;
|
||||
TreeView tree;
|
||||
ScopedPointer<JucerTreeViewBase> rootItem;
|
||||
|
||||
private:
|
||||
String opennessStateKey, emptyTreeMessage;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TreeItemComponent : public Component
|
||||
{
|
||||
public:
|
||||
TreeItemComponent (JucerTreeViewBase& i) : item (i)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setColour (Colours::black);
|
||||
paintIcon (g);
|
||||
item.paintContent (g, Rectangle<int> (item.textX, 0, getWidth() - item.textX, getHeight()));
|
||||
}
|
||||
|
||||
void paintIcon (Graphics& g)
|
||||
{
|
||||
item.getIcon().draw (g, Rectangle<float> (4.0f, 2.0f, item.getIconSize(), getHeight() - 4.0f),
|
||||
item.isIconCrossedOut());
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
item.textX = (int) item.getIconSize() + 8;
|
||||
}
|
||||
|
||||
JucerTreeViewBase& item;
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_JUCERTREEVIEWBASE_JUCEHEADER__
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
String createAlphaNumericUID()
|
||||
{
|
||||
String uid;
|
||||
const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
Random r;
|
||||
|
||||
uid << chars [r.nextInt (52)]; // make sure the first character is always a letter
|
||||
|
||||
for (int i = 5; --i >= 0;)
|
||||
{
|
||||
r.setSeedRandomly();
|
||||
uid << chars [r.nextInt (62)];
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
String hexString8Digits (int value)
|
||||
{
|
||||
return String::toHexString (value).paddedLeft ('0', 8);
|
||||
}
|
||||
|
||||
String createGUID (const String& seed)
|
||||
{
|
||||
const String hex (MD5 ((seed + "_guidsalt").toUTF8()).toHexString().toUpperCase());
|
||||
|
||||
return "{" + hex.substring (0, 8)
|
||||
+ "-" + hex.substring (8, 12)
|
||||
+ "-" + hex.substring (12, 16)
|
||||
+ "-" + hex.substring (16, 20)
|
||||
+ "-" + hex.substring (20, 32)
|
||||
+ "}";
|
||||
}
|
||||
|
||||
String escapeSpaces (const String& s)
|
||||
{
|
||||
return s.replace (" ", "\\ ");
|
||||
}
|
||||
|
||||
String addQuotesIfContainsSpaces (const String& text)
|
||||
{
|
||||
return (text.containsChar (' ') && ! text.isQuotedString()) ? text.quoted() : text;
|
||||
}
|
||||
|
||||
void setValueIfVoid (Value value, const var& defaultValue)
|
||||
{
|
||||
if (value.getValue().isVoid())
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringPairArray parsePreprocessorDefs (const String& text)
|
||||
{
|
||||
StringPairArray result;
|
||||
String::CharPointerType s (text.getCharPointer());
|
||||
|
||||
while (! s.isEmpty())
|
||||
{
|
||||
String token, value;
|
||||
s = s.findEndOfWhitespace();
|
||||
|
||||
while ((! s.isEmpty()) && *s != '=' && ! s.isWhitespace())
|
||||
token << s.getAndAdvance();
|
||||
|
||||
s = s.findEndOfWhitespace();
|
||||
|
||||
if (*s == '=')
|
||||
{
|
||||
++s;
|
||||
|
||||
s = s.findEndOfWhitespace();
|
||||
|
||||
while ((! s.isEmpty()) && ! s.isWhitespace())
|
||||
{
|
||||
if (*s == ',')
|
||||
{
|
||||
++s;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*s == '\\' && (s[1] == ' ' || s[1] == ','))
|
||||
++s;
|
||||
|
||||
value << s.getAndAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
if (token.isNotEmpty())
|
||||
result.set (token, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringPairArray mergePreprocessorDefs (StringPairArray inheritedDefs, const StringPairArray& overridingDefs)
|
||||
{
|
||||
for (int i = 0; i < overridingDefs.size(); ++i)
|
||||
inheritedDefs.set (overridingDefs.getAllKeys()[i], overridingDefs.getAllValues()[i]);
|
||||
|
||||
return inheritedDefs;
|
||||
}
|
||||
|
||||
String createGCCPreprocessorFlags (const StringPairArray& defs)
|
||||
{
|
||||
String s;
|
||||
|
||||
for (int i = 0; i < defs.size(); ++i)
|
||||
{
|
||||
String def (defs.getAllKeys()[i]);
|
||||
const String value (defs.getAllValues()[i]);
|
||||
if (value.isNotEmpty())
|
||||
def << "=" << value;
|
||||
|
||||
if (! def.endsWithChar ('"'))
|
||||
def = def.quoted();
|
||||
|
||||
s += " -D " + def;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString)
|
||||
{
|
||||
for (int i = 0; i < definitions.size(); ++i)
|
||||
{
|
||||
const String key (definitions.getAllKeys()[i]);
|
||||
const String value (definitions.getAllValues()[i]);
|
||||
|
||||
sourceString = sourceString.replace ("${" + key + "}", value);
|
||||
}
|
||||
|
||||
return sourceString;
|
||||
}
|
||||
|
||||
StringArray getSearchPathsFromString (const String& searchPath)
|
||||
{
|
||||
StringArray s;
|
||||
s.addTokens (searchPath, ";\r\n", StringRef());
|
||||
s.trim();
|
||||
s.removeEmptyStrings();
|
||||
s.removeDuplicates (false);
|
||||
return s;
|
||||
}
|
||||
|
||||
void addPlistDictionaryKey (XmlElement* xml, const String& key, const String& value)
|
||||
{
|
||||
forEachXmlChildElementWithTagName (*xml, e, "key")
|
||||
{
|
||||
if (e->getAllSubText().trim().equalsIgnoreCase (key))
|
||||
{
|
||||
if (e->getNextElement() != nullptr && e->getNextElement()->hasTagName ("key"))
|
||||
{
|
||||
// try to fix broken plist format..
|
||||
xml->removeChildElement (e, true);
|
||||
break;
|
||||
}
|
||||
|
||||
return; // (value already exists)
|
||||
}
|
||||
}
|
||||
|
||||
xml->createNewChildElement ("key")->addTextElement (key);
|
||||
xml->createNewChildElement ("string")->addTextElement (value);
|
||||
}
|
||||
|
||||
void addPlistDictionaryKeyBool (XmlElement* xml, const String& key, const bool value)
|
||||
{
|
||||
xml->createNewChildElement ("key")->addTextElement (key);
|
||||
xml->createNewChildElement (value ? "true" : "false");
|
||||
}
|
||||
|
||||
void addPlistDictionaryKeyInt (XmlElement* xml, const String& key, int value)
|
||||
{
|
||||
xml->createNewChildElement ("key")->addTextElement (key);
|
||||
xml->createNewChildElement ("integer")->addTextElement (String (value));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void autoScrollForMouseEvent (const MouseEvent& e, bool scrollX, bool scrollY)
|
||||
{
|
||||
if (Viewport* const viewport = e.eventComponent->findParentComponentOfClass<Viewport>())
|
||||
{
|
||||
const MouseEvent e2 (e.getEventRelativeTo (viewport));
|
||||
viewport->autoScroll (scrollX ? e2.x : 20, scrollY ? e2.y : 20, 8, 16);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int indexOfLineStartingWith (const StringArray& lines, const String& text, int index)
|
||||
{
|
||||
const int len = text.length();
|
||||
|
||||
for (const String* i = lines.begin() + index, * const e = lines.end(); i < e; ++i)
|
||||
{
|
||||
if (CharacterFunctions::compareUpTo (i->getCharPointer().findEndOfWhitespace(),
|
||||
text.getCharPointer(), len) == 0)
|
||||
return index;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
RolloverHelpComp::RolloverHelpComp()
|
||||
: lastComp (nullptr)
|
||||
{
|
||||
setInterceptsMouseClicks (false, false);
|
||||
startTimer (150);
|
||||
}
|
||||
|
||||
void RolloverHelpComp::paint (Graphics& g)
|
||||
{
|
||||
AttributedString s;
|
||||
s.setJustification (Justification::centredLeft);
|
||||
s.append (lastTip, Font (14.0f), findColour (mainBackgroundColourId).contrasting (0.7f));
|
||||
|
||||
TextLayout tl;
|
||||
tl.createLayoutWithBalancedLineLengths (s, getWidth() - 10.0f);
|
||||
if (tl.getNumLines() > 3)
|
||||
tl.createLayout (s, getWidth() - 10.0f);
|
||||
|
||||
tl.draw (g, getLocalBounds().toFloat());
|
||||
}
|
||||
|
||||
void RolloverHelpComp::timerCallback()
|
||||
{
|
||||
Component* newComp = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
|
||||
|
||||
if (newComp != nullptr
|
||||
&& (newComp->getTopLevelComponent() != getTopLevelComponent()
|
||||
|| newComp->isCurrentlyBlockedByAnotherModalComponent()))
|
||||
newComp = nullptr;
|
||||
|
||||
if (newComp != lastComp)
|
||||
{
|
||||
lastComp = newComp;
|
||||
|
||||
String newTip (findTip (newComp));
|
||||
|
||||
if (newTip != lastTip)
|
||||
{
|
||||
lastTip = newTip;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String RolloverHelpComp::findTip (Component* c)
|
||||
{
|
||||
while (c != nullptr)
|
||||
{
|
||||
if (TooltipClient* const tc = dynamic_cast <TooltipClient*> (c))
|
||||
{
|
||||
const String tip (tc->getTooltip());
|
||||
|
||||
if (tip.isNotEmpty())
|
||||
return tip;
|
||||
}
|
||||
|
||||
c = c->getParentComponent();
|
||||
}
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class UTF8Component : public Component,
|
||||
private TextEditorListener
|
||||
{
|
||||
public:
|
||||
UTF8Component()
|
||||
: desc (String::empty,
|
||||
"Type any string into the box, and it'll be shown below as a portable UTF-8 literal, "
|
||||
"ready to cut-and-paste into your source-code...")
|
||||
{
|
||||
desc.setJustificationType (Justification::centred);
|
||||
desc.setColour (Label::textColourId, Colours::white);
|
||||
addAndMakeVisible (desc);
|
||||
|
||||
const Colour bkgd (Colours::white.withAlpha (0.6f));
|
||||
|
||||
userText.setMultiLine (true, true);
|
||||
userText.setReturnKeyStartsNewLine (true);
|
||||
userText.setColour (TextEditor::backgroundColourId, bkgd);
|
||||
addAndMakeVisible (userText);
|
||||
userText.addListener (this);
|
||||
|
||||
resultText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
resultText.setMultiLine (true, true);
|
||||
resultText.setColour (TextEditor::backgroundColourId, bkgd);
|
||||
resultText.setReadOnly (true);
|
||||
resultText.setSelectAllWhenFocused (true);
|
||||
addAndMakeVisible (resultText);
|
||||
|
||||
userText.setText (getLastText());
|
||||
}
|
||||
|
||||
void textEditorTextChanged (TextEditor&)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void textEditorEscapeKeyPressed (TextEditor&)
|
||||
{
|
||||
getTopLevelComponent()->exitModalState (0);
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
getLastText() = userText.getText();
|
||||
resultText.setText (CodeHelpers::stringLiteral (getLastText(), 100), false);
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds().reduced (8));
|
||||
desc.setBounds (r.removeFromTop (44));
|
||||
r.removeFromTop (8);
|
||||
userText.setBounds (r.removeFromTop (r.getHeight() / 2));
|
||||
r.removeFromTop (8);
|
||||
resultText.setBounds (r);
|
||||
}
|
||||
|
||||
private:
|
||||
Label desc;
|
||||
TextEditor userText, resultText;
|
||||
|
||||
String& getLastText()
|
||||
{
|
||||
static String t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
void showUTF8ToolWindow (ScopedPointer<Component>& ownerPointer)
|
||||
{
|
||||
if (ownerPointer != nullptr)
|
||||
{
|
||||
ownerPointer->toFront (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
new FloatingToolWindow ("UTF-8 String Literal Converter",
|
||||
"utf8WindowPos",
|
||||
new UTF8Component(), ownerPointer,
|
||||
500, 500,
|
||||
300, 300, 1000, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SVGPathDataComponent : public Component,
|
||||
private TextEditorListener
|
||||
{
|
||||
public:
|
||||
SVGPathDataComponent()
|
||||
: desc (String::empty,
|
||||
"Paste an SVG path string into the top box, and it'll be converted to some C++ "
|
||||
"code that will load it as a Path object..")
|
||||
{
|
||||
desc.setJustificationType (Justification::centred);
|
||||
desc.setColour (Label::textColourId, Colours::white);
|
||||
addAndMakeVisible (desc);
|
||||
|
||||
const Colour bkgd (Colours::white.withAlpha (0.6f));
|
||||
|
||||
userText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
userText.setMultiLine (true, true);
|
||||
userText.setReturnKeyStartsNewLine (true);
|
||||
userText.setColour (TextEditor::backgroundColourId, bkgd);
|
||||
addAndMakeVisible (userText);
|
||||
userText.addListener (this);
|
||||
|
||||
resultText.setFont (getAppSettings().appearance.getCodeFont().withHeight (13.0f));
|
||||
resultText.setMultiLine (true, true);
|
||||
resultText.setColour (TextEditor::backgroundColourId, bkgd);
|
||||
resultText.setReadOnly (true);
|
||||
resultText.setSelectAllWhenFocused (true);
|
||||
addAndMakeVisible (resultText);
|
||||
|
||||
userText.setText (getLastText());
|
||||
}
|
||||
|
||||
void textEditorTextChanged (TextEditor&)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void textEditorEscapeKeyPressed (TextEditor&)
|
||||
{
|
||||
getTopLevelComponent()->exitModalState (0);
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
getLastText() = userText.getText();
|
||||
|
||||
path = Drawable::parseSVGPath (getLastText().trim().unquoted().trim());
|
||||
|
||||
String result = "No path generated.. Not a valid SVG path string?";
|
||||
|
||||
if (! path.isEmpty())
|
||||
{
|
||||
MemoryOutputStream data;
|
||||
path.writePathToStream (data);
|
||||
|
||||
MemoryOutputStream out;
|
||||
|
||||
out << "static const unsigned char pathData[] = ";
|
||||
CodeHelpers::writeDataAsCppLiteral (data.getMemoryBlock(), out, false, true);
|
||||
out << newLine
|
||||
<< newLine
|
||||
<< "Path path;" << newLine
|
||||
<< "path.loadPathFromData (pathData, sizeof (pathData));" << newLine;
|
||||
|
||||
result = out.toString();
|
||||
}
|
||||
|
||||
resultText.setText (result, false);
|
||||
repaint (previewPathArea);
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
Rectangle<int> r (getLocalBounds().reduced (8));
|
||||
desc.setBounds (r.removeFromTop (44));
|
||||
r.removeFromTop (8);
|
||||
userText.setBounds (r.removeFromTop (r.getHeight() / 2));
|
||||
r.removeFromTop (8);
|
||||
previewPathArea = r.removeFromRight (r.getHeight());
|
||||
resultText.setBounds (r);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::white);
|
||||
g.fillPath (path, path.getTransformToScaleToFit (previewPathArea.reduced (4).toFloat(), true));
|
||||
}
|
||||
|
||||
private:
|
||||
Label desc;
|
||||
TextEditor userText, resultText;
|
||||
Rectangle<int> previewPathArea;
|
||||
Path path;
|
||||
|
||||
String& getLastText()
|
||||
{
|
||||
static String t;
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
void showSVGPathDataToolWindow (ScopedPointer<Component>& ownerPointer)
|
||||
{
|
||||
if (ownerPointer != nullptr)
|
||||
ownerPointer->toFront (true);
|
||||
else
|
||||
new FloatingToolWindow ("SVG Path Converter",
|
||||
"svgPathWindowPos",
|
||||
new SVGPathDataComponent(), ownerPointer,
|
||||
500, 500,
|
||||
300, 300, 1000, 1000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AsyncCommandRetrier : public Timer
|
||||
{
|
||||
public:
|
||||
AsyncCommandRetrier (const ApplicationCommandTarget::InvocationInfo& inf)
|
||||
: info (inf)
|
||||
{
|
||||
info.originatingComponent = nullptr;
|
||||
startTimer (500);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
IntrojucerApp::getCommandManager().invoke (info, true);
|
||||
delete this;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget::InvocationInfo info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AsyncCommandRetrier)
|
||||
};
|
||||
|
||||
bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo& info)
|
||||
{
|
||||
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
||||
{
|
||||
new AsyncCommandRetrier (info);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
String hexString8Digits (int value);
|
||||
|
||||
String createAlphaNumericUID();
|
||||
String createGUID (const String& seed); // Turns a seed into a windows GUID
|
||||
|
||||
String escapeSpaces (const String& text); // replaces spaces with blackslash-space
|
||||
String addQuotesIfContainsSpaces (const String& text);
|
||||
|
||||
StringPairArray parsePreprocessorDefs (const String& defs);
|
||||
StringPairArray mergePreprocessorDefs (StringPairArray inheritedDefs, const StringPairArray& overridingDefs);
|
||||
String createGCCPreprocessorFlags (const StringPairArray& defs);
|
||||
String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString);
|
||||
|
||||
StringArray getSearchPathsFromString (const String& searchPath);
|
||||
|
||||
void setValueIfVoid (Value value, const var& defaultValue);
|
||||
|
||||
void addPlistDictionaryKey (XmlElement* xml, const String& key, const String& value);
|
||||
void addPlistDictionaryKeyBool (XmlElement* xml, const String& key, bool value);
|
||||
void addPlistDictionaryKeyInt (XmlElement* xml, const String& key, int value);
|
||||
|
||||
//==============================================================================
|
||||
int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex);
|
||||
|
||||
void autoScrollForMouseEvent (const MouseEvent& e, bool scrollX = true, bool scrollY = true);
|
||||
|
||||
void showUTF8ToolWindow (ScopedPointer<Component>& ownerPointer);
|
||||
void showSVGPathDataToolWindow (ScopedPointer<Component>& ownerPointer);
|
||||
|
||||
bool cancelAnyModalComponents();
|
||||
bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo&);
|
||||
|
||||
//==============================================================================
|
||||
class RolloverHelpComp : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
RolloverHelpComp();
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void timerCallback() override;
|
||||
|
||||
private:
|
||||
Component* lastComp;
|
||||
String lastTip;
|
||||
|
||||
static String findTip (Component*);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class PropertyListBuilder
|
||||
{
|
||||
public:
|
||||
PropertyListBuilder() {}
|
||||
|
||||
void add (PropertyComponent* propertyComp)
|
||||
{
|
||||
components.add (propertyComp);
|
||||
}
|
||||
|
||||
void add (PropertyComponent* propertyComp, const String& tooltip)
|
||||
{
|
||||
propertyComp->setTooltip (tooltip);
|
||||
add (propertyComp);
|
||||
}
|
||||
|
||||
void addSearchPathProperty (const Value& value, const String& name, const String& mainHelpText)
|
||||
{
|
||||
add (new TextPropertyComponent (value, name, 16384, true),
|
||||
mainHelpText + " Use semi-colons or new-lines to separate multiple paths.");
|
||||
}
|
||||
|
||||
void setPreferredHeight (int height)
|
||||
{
|
||||
for (int j = components.size(); --j >= 0;)
|
||||
components.getUnchecked(j)->setPreferredHeight (height);
|
||||
}
|
||||
|
||||
Array <PropertyComponent*> components;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyListBuilder)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// A ValueSource which takes an input source, and forwards any changes in it.
|
||||
// This class is a handy way to create sources which re-map a value.
|
||||
class ValueSourceFilter : public Value::ValueSource,
|
||||
public Value::Listener
|
||||
{
|
||||
public:
|
||||
ValueSourceFilter (const Value& source) : sourceValue (source)
|
||||
{
|
||||
sourceValue.addListener (this);
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override { sendChangeMessage (true); }
|
||||
|
||||
protected:
|
||||
Value sourceValue;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSourceFilter)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FloatingToolWindow : public DialogWindow
|
||||
{
|
||||
public:
|
||||
FloatingToolWindow (const String& title,
|
||||
const String& windowPosPropertyName,
|
||||
Component* content,
|
||||
ScopedPointer<Component>& ownerPointer,
|
||||
int defaultW, int defaultH,
|
||||
int minW, int minH,
|
||||
int maxW, int maxH)
|
||||
: DialogWindow (title, Colours::darkgrey, true, true),
|
||||
windowPosProperty (windowPosPropertyName),
|
||||
owner (ownerPointer)
|
||||
{
|
||||
setUsingNativeTitleBar (true);
|
||||
setResizable (true, true);
|
||||
setResizeLimits (minW, minH, maxW, maxH);
|
||||
setContentOwned (content, false);
|
||||
|
||||
const String windowState (getGlobalProperties().getValue (windowPosProperty));
|
||||
|
||||
if (windowState.isNotEmpty())
|
||||
restoreWindowStateFromString (windowState);
|
||||
else
|
||||
centreAroundComponent (Component::getCurrentlyFocusedComponent(), defaultW, defaultH);
|
||||
|
||||
setVisible (true);
|
||||
owner = this;
|
||||
}
|
||||
|
||||
~FloatingToolWindow()
|
||||
{
|
||||
getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
owner = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
String windowPosProperty;
|
||||
ScopedPointer<Component>& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FloatingToolWindow)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class PopupColourSelector : public Component,
|
||||
public ChangeListener,
|
||||
public Value::Listener,
|
||||
public ButtonListener
|
||||
{
|
||||
public:
|
||||
PopupColourSelector (const Value& colour,
|
||||
Colour defaultCol,
|
||||
const bool canResetToDefault)
|
||||
: defaultButton ("Reset to Default"),
|
||||
colourValue (colour),
|
||||
defaultColour (defaultCol)
|
||||
{
|
||||
addAndMakeVisible (selector);
|
||||
selector.setName ("Colour");
|
||||
selector.setCurrentColour (getColour());
|
||||
selector.addChangeListener (this);
|
||||
|
||||
if (canResetToDefault)
|
||||
{
|
||||
addAndMakeVisible (defaultButton);
|
||||
defaultButton.addListener (this);
|
||||
}
|
||||
|
||||
colourValue.addListener (this);
|
||||
setSize (300, 400);
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
if (defaultButton.isVisible())
|
||||
{
|
||||
selector.setBounds (0, 0, getWidth(), getHeight() - 30);
|
||||
defaultButton.changeWidthToFitText (22);
|
||||
defaultButton.setTopLeftPosition (10, getHeight() - 26);
|
||||
}
|
||||
else
|
||||
{
|
||||
selector.setBounds (getLocalBounds());
|
||||
}
|
||||
}
|
||||
|
||||
Colour getColour() const
|
||||
{
|
||||
if (colourValue.toString().isEmpty())
|
||||
return defaultColour;
|
||||
|
||||
return Colour::fromString (colourValue.toString());
|
||||
}
|
||||
|
||||
void setColour (Colour newColour)
|
||||
{
|
||||
if (getColour() != newColour)
|
||||
{
|
||||
if (newColour == defaultColour && defaultButton.isVisible())
|
||||
colourValue = var();
|
||||
else
|
||||
colourValue = newColour.toDisplayString (true);
|
||||
}
|
||||
}
|
||||
|
||||
void buttonClicked (Button*) override
|
||||
{
|
||||
setColour (defaultColour);
|
||||
selector.setCurrentColour (defaultColour);
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
if (selector.getCurrentColour() != getColour())
|
||||
setColour (selector.getCurrentColour());
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
selector.setCurrentColour (getColour());
|
||||
}
|
||||
|
||||
private:
|
||||
StoredSettings::ColourSelectorWithSwatches selector;
|
||||
TextButton defaultButton;
|
||||
Value colourValue;
|
||||
Colour defaultColour;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that shows a colour swatch with hex ARGB value, and which pops up
|
||||
a colour selector when you click it.
|
||||
*/
|
||||
class ColourEditorComponent : public Component,
|
||||
public Value::Listener
|
||||
{
|
||||
public:
|
||||
ColourEditorComponent (UndoManager* um, const Value& colour,
|
||||
Colour defaultCol, const bool canReset)
|
||||
: undoManager (um), colourValue (colour), defaultColour (defaultCol),
|
||||
canResetToDefault (canReset)
|
||||
{
|
||||
colourValue.addListener (this);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
const Colour colour (getColour());
|
||||
|
||||
g.fillAll (Colours::grey);
|
||||
g.fillCheckerBoard (getLocalBounds().reduced (2),
|
||||
10, 10,
|
||||
Colour (0xffdddddd).overlaidWith (colour),
|
||||
Colour (0xffffffff).overlaidWith (colour));
|
||||
|
||||
g.setColour (Colours::white.overlaidWith (colour).contrasting());
|
||||
g.setFont (Font (getHeight() * 0.6f, Font::bold));
|
||||
g.drawFittedText (colour.toDisplayString (true), getLocalBounds().reduced (2, 1),
|
||||
Justification::centred, 1);
|
||||
}
|
||||
|
||||
Colour getColour() const
|
||||
{
|
||||
if (colourValue.toString().isEmpty())
|
||||
return defaultColour;
|
||||
|
||||
return Colour::fromString (colourValue.toString());
|
||||
}
|
||||
|
||||
void setColour (Colour newColour)
|
||||
{
|
||||
if (getColour() != newColour)
|
||||
{
|
||||
if (newColour == defaultColour && canResetToDefault)
|
||||
colourValue = var::null;
|
||||
else
|
||||
colourValue = newColour.toDisplayString (true);
|
||||
}
|
||||
}
|
||||
|
||||
void resetToDefault()
|
||||
{
|
||||
setColour (defaultColour);
|
||||
}
|
||||
|
||||
void refresh()
|
||||
{
|
||||
const Colour col (getColour());
|
||||
|
||||
if (col != lastColour)
|
||||
{
|
||||
lastColour = col;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
if (undoManager != nullptr)
|
||||
undoManager->beginNewTransaction();
|
||||
|
||||
CallOutBox::launchAsynchronously (new PopupColourSelector (colourValue,
|
||||
defaultColour,
|
||||
canResetToDefault),
|
||||
getScreenBounds(), nullptr);
|
||||
}
|
||||
|
||||
void valueChanged (Value&) override
|
||||
{
|
||||
refresh();
|
||||
}
|
||||
|
||||
private:
|
||||
UndoManager* undoManager;
|
||||
Value colourValue;
|
||||
Colour lastColour;
|
||||
const Colour defaultColour;
|
||||
const bool canResetToDefault;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourEditorComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ColourPropertyComponent : public PropertyComponent
|
||||
{
|
||||
public:
|
||||
ColourPropertyComponent (UndoManager* undoManager, const String& name, const Value& colour,
|
||||
Colour defaultColour, bool canResetToDefault)
|
||||
: PropertyComponent (name),
|
||||
colourEditor (undoManager, colour, defaultColour, canResetToDefault)
|
||||
{
|
||||
addAndMakeVisible (colourEditor);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
colourEditor.setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this));
|
||||
}
|
||||
|
||||
void refresh() override {}
|
||||
|
||||
protected:
|
||||
ColourEditorComponent colourEditor;
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_PRESETIDS_JUCEHEADER__
|
||||
#define __JUCER_PRESETIDS_JUCEHEADER__
|
||||
|
||||
|
||||
// Handy list of static Identifiers..
|
||||
namespace Ids
|
||||
{
|
||||
#define DECLARE_ID(name) const Identifier name (#name)
|
||||
|
||||
DECLARE_ID (name);
|
||||
DECLARE_ID (file);
|
||||
DECLARE_ID (path);
|
||||
DECLARE_ID (text);
|
||||
DECLARE_ID (version);
|
||||
DECLARE_ID (license);
|
||||
DECLARE_ID (include);
|
||||
DECLARE_ID (info);
|
||||
DECLARE_ID (description);
|
||||
DECLARE_ID (companyName);
|
||||
DECLARE_ID (companyWebsite);
|
||||
DECLARE_ID (companyEmail);
|
||||
DECLARE_ID (position);
|
||||
DECLARE_ID (source);
|
||||
DECLARE_ID (width);
|
||||
DECLARE_ID (height);
|
||||
DECLARE_ID (background);
|
||||
DECLARE_ID (initialState);
|
||||
DECLARE_ID (targetFolder);
|
||||
DECLARE_ID (intermediatesPath);
|
||||
DECLARE_ID (vstFolder);
|
||||
DECLARE_ID (vst3Folder);
|
||||
DECLARE_ID (rtasFolder);
|
||||
DECLARE_ID (auFolder);
|
||||
DECLARE_ID (flags);
|
||||
DECLARE_ID (line);
|
||||
DECLARE_ID (index);
|
||||
DECLARE_ID (type);
|
||||
DECLARE_ID (time);
|
||||
DECLARE_ID (extraCompilerFlags);
|
||||
DECLARE_ID (extraLinkerFlags);
|
||||
DECLARE_ID (externalLibraries);
|
||||
DECLARE_ID (extraDefs);
|
||||
DECLARE_ID (projectType);
|
||||
DECLARE_ID (isDebug);
|
||||
DECLARE_ID (alwaysGenerateDebugSymbols);
|
||||
DECLARE_ID (targetName);
|
||||
DECLARE_ID (binaryPath);
|
||||
DECLARE_ID (optimisation);
|
||||
DECLARE_ID (defines);
|
||||
DECLARE_ID (headerPath);
|
||||
DECLARE_ID (systemHeaderPath);
|
||||
DECLARE_ID (libraryPath);
|
||||
DECLARE_ID (customXcodeFlags);
|
||||
DECLARE_ID (cppLibType);
|
||||
DECLARE_ID (codeSigningIdentity);
|
||||
DECLARE_ID (fastMath);
|
||||
DECLARE_ID (linkTimeOptimisation);
|
||||
DECLARE_ID (osxSDK);
|
||||
DECLARE_ID (osxCompatibility);
|
||||
DECLARE_ID (osxArchitecture);
|
||||
DECLARE_ID (iosCompatibility);
|
||||
DECLARE_ID (extraFrameworks);
|
||||
DECLARE_ID (extraDLLs);
|
||||
DECLARE_ID (winArchitecture);
|
||||
DECLARE_ID (winWarningLevel);
|
||||
DECLARE_ID (linuxArchitecture);
|
||||
DECLARE_ID (toolset);
|
||||
DECLARE_ID (msvcModuleDefinitionFile);
|
||||
DECLARE_ID (bigIcon);
|
||||
DECLARE_ID (smallIcon);
|
||||
DECLARE_ID (jucerVersion);
|
||||
DECLARE_ID (prebuildCommand);
|
||||
DECLARE_ID (postbuildCommand);
|
||||
DECLARE_ID (generateManifest);
|
||||
DECLARE_ID (useRuntimeLibDLL);
|
||||
DECLARE_ID (wholeProgramOptimisation);
|
||||
DECLARE_ID (juceLinkage);
|
||||
DECLARE_ID (buildVST);
|
||||
DECLARE_ID (bundleIdentifier);
|
||||
DECLARE_ID (aaxIdentifier);
|
||||
DECLARE_ID (aaxCategory);
|
||||
DECLARE_ID (aaxFolder);
|
||||
DECLARE_ID (compile);
|
||||
DECLARE_ID (noWarnings);
|
||||
DECLARE_ID (resource);
|
||||
DECLARE_ID (className);
|
||||
DECLARE_ID (classDesc);
|
||||
DECLARE_ID (controlPoint);
|
||||
DECLARE_ID (createCallback);
|
||||
DECLARE_ID (parentClasses);
|
||||
DECLARE_ID (constructorParams);
|
||||
DECLARE_ID (objectConstructionArgs);
|
||||
DECLARE_ID (memberInitialisers);
|
||||
DECLARE_ID (canBeAggregated);
|
||||
DECLARE_ID (rootItemVisible);
|
||||
DECLARE_ID (openByDefault);
|
||||
DECLARE_ID (locked);
|
||||
DECLARE_ID (tooltip);
|
||||
DECLARE_ID (memberName);
|
||||
DECLARE_ID (markerName);
|
||||
DECLARE_ID (focusOrder);
|
||||
DECLARE_ID (hidden);
|
||||
DECLARE_ID (useStdCall);
|
||||
DECLARE_ID (showAllCode);
|
||||
DECLARE_ID (useLocalCopy);
|
||||
DECLARE_ID (androidActivityClass);
|
||||
DECLARE_ID (androidSDKPath);
|
||||
DECLARE_ID (androidNDKPath);
|
||||
DECLARE_ID (androidInternetNeeded);
|
||||
DECLARE_ID (androidArchitectures);
|
||||
DECLARE_ID (androidCpp11);
|
||||
DECLARE_ID (androidMicNeeded);
|
||||
DECLARE_ID (androidMinimumSDK);
|
||||
DECLARE_ID (androidOtherPermissions);
|
||||
DECLARE_ID (androidKeyStore);
|
||||
DECLARE_ID (androidKeyStorePass);
|
||||
DECLARE_ID (androidKeyAlias);
|
||||
DECLARE_ID (androidKeyAliasPass);
|
||||
DECLARE_ID (font);
|
||||
DECLARE_ID (colour);
|
||||
DECLARE_ID (userNotes);
|
||||
DECLARE_ID (maxBinaryFileSize);
|
||||
DECLARE_ID (includeBinaryInAppConfig);
|
||||
DECLARE_ID (characterSet);
|
||||
DECLARE_ID (JUCERPROJECT);
|
||||
DECLARE_ID (MAINGROUP);
|
||||
DECLARE_ID (EXPORTFORMATS);
|
||||
DECLARE_ID (GROUP);
|
||||
DECLARE_ID (FILE);
|
||||
DECLARE_ID (MODULES);
|
||||
DECLARE_ID (MODULE);
|
||||
DECLARE_ID (JUCEOPTIONS);
|
||||
DECLARE_ID (CONFIGURATIONS);
|
||||
DECLARE_ID (CONFIGURATION);
|
||||
DECLARE_ID (MODULEPATHS);
|
||||
DECLARE_ID (MODULEPATH);
|
||||
|
||||
const Identifier ID ("id");
|
||||
const Identifier class_ ("class");
|
||||
|
||||
#undef DECLARE_ID
|
||||
}
|
||||
|
||||
#endif // __JUCER_PRESETIDS_JUCEHEADER__
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_RELATIVEPATH_JUCEHEADER__
|
||||
#define __JUCER_RELATIVEPATH_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Manipulates a cross-platform partial file path. (Needed because File is designed
|
||||
for absolute paths on the active OS)
|
||||
*/
|
||||
class RelativePath
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
enum RootFolder
|
||||
{
|
||||
unknown,
|
||||
projectFolder,
|
||||
buildTargetFolder
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
RelativePath()
|
||||
: root (unknown)
|
||||
{}
|
||||
|
||||
RelativePath (const String& relPath, const RootFolder rootType)
|
||||
: path (FileHelpers::unixStylePath (relPath)), root (rootType)
|
||||
{
|
||||
}
|
||||
|
||||
RelativePath (const File& file, const File& rootFolder, const RootFolder rootType)
|
||||
: path (FileHelpers::unixStylePath (FileHelpers::getRelativePathFrom (file, rootFolder))), root (rootType)
|
||||
{
|
||||
}
|
||||
|
||||
RootFolder getRoot() const { return root; }
|
||||
|
||||
String toUnixStyle() const { return FileHelpers::unixStylePath (path); }
|
||||
String toWindowsStyle() const { return FileHelpers::windowsStylePath (path); }
|
||||
|
||||
String getFileName() const { return getFakeFile().getFileName(); }
|
||||
String getFileNameWithoutExtension() const { return getFakeFile().getFileNameWithoutExtension(); }
|
||||
|
||||
String getFileExtension() const { return getFakeFile().getFileExtension(); }
|
||||
bool hasFileExtension (const String& extension) const { return getFakeFile().hasFileExtension (extension); }
|
||||
bool isAbsolute() const { return FileHelpers::isAbsolutePath (path); }
|
||||
|
||||
RelativePath withFileExtension (const String& extension) const
|
||||
{
|
||||
return RelativePath (path.upToLastOccurrenceOf (".", ! extension.startsWithChar ('.'), false) + extension, root);
|
||||
}
|
||||
|
||||
RelativePath getParentDirectory() const
|
||||
{
|
||||
String p (path);
|
||||
if (path.endsWithChar ('/'))
|
||||
p = p.dropLastCharacters (1);
|
||||
|
||||
return RelativePath (p.upToLastOccurrenceOf ("/", false, false), root);
|
||||
}
|
||||
|
||||
RelativePath getChildFile (const String& subpath) const
|
||||
{
|
||||
if (FileHelpers::isAbsolutePath (subpath))
|
||||
return RelativePath (subpath, root);
|
||||
|
||||
String p (toUnixStyle());
|
||||
if (! p.endsWithChar ('/'))
|
||||
p << '/';
|
||||
|
||||
return RelativePath (p + subpath, root);
|
||||
}
|
||||
|
||||
RelativePath rebased (const File& originalRoot, const File& newRoot, const RootFolder newRootType) const
|
||||
{
|
||||
if (isAbsolute())
|
||||
return RelativePath (path, newRootType);
|
||||
|
||||
return RelativePath (FileHelpers::getRelativePathFrom (originalRoot.getChildFile (toUnixStyle()), newRoot), newRootType);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String path;
|
||||
RootFolder root;
|
||||
|
||||
File getFakeFile() const
|
||||
{
|
||||
// This method gets called very often, so we'll cache this directory.
|
||||
static const File currentWorkingDirectory (File::getCurrentWorkingDirectory());
|
||||
return currentWorkingDirectory.getChildFile (path);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_RELATIVEPATH_JUCEHEADER__
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
#include "jucer_SlidingPanelComponent.h"
|
||||
|
||||
|
||||
struct SlidingPanelComponent::DotButton : public Button
|
||||
{
|
||||
DotButton (SlidingPanelComponent& sp, int pageIndex)
|
||||
: Button (String()), owner (sp), index (pageIndex) {}
|
||||
|
||||
void paintButton (Graphics& g, bool /*isMouseOverButton*/, bool /*isButtonDown*/) override
|
||||
{
|
||||
g.setColour (Colours::white);
|
||||
const Rectangle<float> r (getLocalBounds().reduced (getWidth() / 4).toFloat());
|
||||
|
||||
if (index == owner.getCurrentTabIndex())
|
||||
g.fillEllipse (r);
|
||||
else
|
||||
g.drawEllipse (r, 1.0f);
|
||||
}
|
||||
|
||||
void clicked() override
|
||||
{
|
||||
owner.goToTab (index);
|
||||
}
|
||||
|
||||
SlidingPanelComponent& owner;
|
||||
int index;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
SlidingPanelComponent::SlidingPanelComponent()
|
||||
: currentIndex (0), dotSize (20)
|
||||
{
|
||||
addAndMakeVisible (pageHolder);
|
||||
}
|
||||
|
||||
SlidingPanelComponent::~SlidingPanelComponent()
|
||||
{
|
||||
}
|
||||
|
||||
SlidingPanelComponent::PageInfo::~PageInfo()
|
||||
{
|
||||
if (shouldDelete)
|
||||
content.deleteAndZero();
|
||||
}
|
||||
|
||||
void SlidingPanelComponent::addTab (const String& tabName,
|
||||
Component* const contentComponent,
|
||||
const bool deleteComponentWhenNotNeeded,
|
||||
const int insertIndex)
|
||||
{
|
||||
PageInfo* page = new PageInfo();
|
||||
pages.insert (insertIndex, page);
|
||||
page->content = contentComponent;
|
||||
addAndMakeVisible (page->dotButton = new DotButton (*this, pages.indexOf (page)));
|
||||
page->name = tabName;
|
||||
page->shouldDelete = deleteComponentWhenNotNeeded;
|
||||
|
||||
pageHolder.addAndMakeVisible (contentComponent);
|
||||
resized();
|
||||
}
|
||||
|
||||
void SlidingPanelComponent::goToTab (int targetTabIndex)
|
||||
{
|
||||
const int xTranslation = (currentIndex - targetTabIndex) * getWidth();
|
||||
|
||||
currentIndex = targetTabIndex;
|
||||
|
||||
Desktop::getInstance().getAnimator()
|
||||
.animateComponent (&pageHolder, pageHolder.getBounds().translated (xTranslation, 0),
|
||||
1.0f, 600, false, 0.0, 0.0);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void SlidingPanelComponent::resized()
|
||||
{
|
||||
pageHolder.setBounds (-currentIndex * getWidth(), pageHolder.getPosition().y,
|
||||
getNumTabs() * getWidth(), getHeight());
|
||||
|
||||
Rectangle<int> content (getLocalBounds());
|
||||
|
||||
Rectangle<int> dotHolder = content.removeFromBottom (20 + dotSize)
|
||||
.reduced ((content.getWidth() - dotSize * getNumTabs()) / 2, 10);
|
||||
|
||||
for (int i = 0; i < getNumTabs(); ++i)
|
||||
pages.getUnchecked(i)->dotButton->setBounds (dotHolder.removeFromLeft (dotSize));
|
||||
|
||||
for (int i = pages.size(); --i >= 0;)
|
||||
if (Component* c = pages.getUnchecked(i)->content)
|
||||
c->setBounds (content.translated (i * content.getWidth(), 0));
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCER_SLIDINGPANELCOMPONENT_H_INCLUDED
|
||||
#define JUCER_SLIDINGPANELCOMPONENT_H_INCLUDED
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class SlidingPanelComponent : public Component
|
||||
{
|
||||
public:
|
||||
SlidingPanelComponent();
|
||||
~SlidingPanelComponent();
|
||||
|
||||
/** Adds a new tab to the panel slider. */
|
||||
void addTab (const String& tabName,
|
||||
Component* contentComponent,
|
||||
bool deleteComponentWhenNotNeeded,
|
||||
int insertIndex = -1);
|
||||
|
||||
/** Gets rid of one of the tabs. */
|
||||
void removeTab (int tabIndex);
|
||||
|
||||
/** Gets index of current tab. */
|
||||
int getCurrentTabIndex() const noexcept { return currentIndex; }
|
||||
|
||||
/** Returns the number of tabs. */
|
||||
int getNumTabs() const noexcept { return pages.size(); }
|
||||
|
||||
/** Animates the window to the desired tab. */
|
||||
void goToTab (int targetTabIndex);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
struct DotButton;
|
||||
friend struct DotButton;
|
||||
|
||||
struct PageInfo
|
||||
{
|
||||
~PageInfo();
|
||||
|
||||
Component::SafePointer<Component> content;
|
||||
ScopedPointer<DotButton> dotButton;
|
||||
String name;
|
||||
bool shouldDelete;
|
||||
};
|
||||
|
||||
OwnedArray<PageInfo> pages;
|
||||
|
||||
Component pageHolder;
|
||||
int currentIndex, dotSize;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlidingPanelComponent);
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCER_SLIDINGPANELCOMPONENT_H_INCLUDED
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "jucer_StoredSettings.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
StoredSettings& getAppSettings()
|
||||
{
|
||||
return *IntrojucerApp::getApp().settings;
|
||||
}
|
||||
|
||||
PropertiesFile& getGlobalProperties()
|
||||
{
|
||||
return getAppSettings().getGlobalProperties();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StoredSettings::StoredSettings()
|
||||
: appearance (true)
|
||||
{
|
||||
reload();
|
||||
}
|
||||
|
||||
StoredSettings::~StoredSettings()
|
||||
{
|
||||
flush();
|
||||
}
|
||||
|
||||
PropertiesFile& StoredSettings::getGlobalProperties()
|
||||
{
|
||||
return *propertyFiles.getUnchecked (0);
|
||||
}
|
||||
|
||||
static PropertiesFile* createPropsFile (const String& filename)
|
||||
{
|
||||
return new PropertiesFile (IntrojucerApp::getApp()
|
||||
.getPropertyFileOptionsFor (filename));
|
||||
}
|
||||
|
||||
PropertiesFile& StoredSettings::getProjectProperties (const String& projectUID)
|
||||
{
|
||||
const String filename ("Introjucer_Project_" + projectUID);
|
||||
|
||||
for (int i = propertyFiles.size(); --i >= 0;)
|
||||
{
|
||||
PropertiesFile* const props = propertyFiles.getUnchecked(i);
|
||||
if (props->getFile().getFileNameWithoutExtension() == filename)
|
||||
return *props;
|
||||
}
|
||||
|
||||
PropertiesFile* p = createPropsFile (filename);
|
||||
propertyFiles.add (p);
|
||||
return *p;
|
||||
}
|
||||
|
||||
void StoredSettings::updateGlobalProps()
|
||||
{
|
||||
PropertiesFile& props = getGlobalProperties();
|
||||
|
||||
{
|
||||
const ScopedPointer<XmlElement> xml (appearance.settings.createXml());
|
||||
props.setValue ("editorColours", xml);
|
||||
}
|
||||
|
||||
props.setValue ("recentFiles", recentFiles.toString());
|
||||
|
||||
props.removeValue ("keyMappings");
|
||||
|
||||
if (ApplicationCommandManager* commandManager = IntrojucerApp::getApp().commandManager)
|
||||
{
|
||||
const ScopedPointer <XmlElement> keys (commandManager->getKeyMappings()->createXml (true));
|
||||
|
||||
if (keys != nullptr)
|
||||
props.setValue ("keyMappings", keys);
|
||||
}
|
||||
}
|
||||
|
||||
void StoredSettings::flush()
|
||||
{
|
||||
updateGlobalProps();
|
||||
saveSwatchColours();
|
||||
|
||||
for (int i = propertyFiles.size(); --i >= 0;)
|
||||
propertyFiles.getUnchecked(i)->saveIfNeeded();
|
||||
}
|
||||
|
||||
void StoredSettings::reload()
|
||||
{
|
||||
propertyFiles.clear();
|
||||
propertyFiles.add (createPropsFile ("Introjucer"));
|
||||
|
||||
// recent files...
|
||||
recentFiles.restoreFromString (getGlobalProperties().getValue ("recentFiles"));
|
||||
recentFiles.removeNonExistentFiles();
|
||||
|
||||
ScopedPointer<XmlElement> xml (getGlobalProperties().getXmlValue ("editorColours"));
|
||||
|
||||
if (xml == nullptr)
|
||||
{
|
||||
xml = XmlDocument::parse (BinaryData::colourscheme_dark_xml);
|
||||
jassert (xml != nullptr);
|
||||
}
|
||||
|
||||
appearance.readFromXML (*xml);
|
||||
|
||||
appearance.updateColourScheme();
|
||||
loadSwatchColours();
|
||||
}
|
||||
|
||||
Array<File> StoredSettings::getLastProjects()
|
||||
{
|
||||
StringArray s;
|
||||
s.addTokens (getGlobalProperties().getValue ("lastProjects"), "|", "");
|
||||
|
||||
Array<File> f;
|
||||
for (int i = 0; i < s.size(); ++i)
|
||||
f.add (File (s[i]));
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void StoredSettings::setLastProjects (const Array<File>& files)
|
||||
{
|
||||
StringArray s;
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
s.add (files.getReference(i).getFullPathName());
|
||||
|
||||
getGlobalProperties().setValue ("lastProjects", s.joinIntoString ("|"));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void StoredSettings::loadSwatchColours()
|
||||
{
|
||||
swatchColours.clear();
|
||||
|
||||
#define COL(col) Colours::col,
|
||||
|
||||
const Colour colours[] =
|
||||
{
|
||||
#include "jucer_Colours.h"
|
||||
Colours::transparentBlack
|
||||
};
|
||||
|
||||
#undef COL
|
||||
|
||||
const int numSwatchColours = 24;
|
||||
PropertiesFile& props = getGlobalProperties();
|
||||
|
||||
for (int i = 0; i < numSwatchColours; ++i)
|
||||
swatchColours.add (Colour::fromString (props.getValue ("swatchColour" + String (i),
|
||||
colours [2 + i].toString())));
|
||||
}
|
||||
|
||||
void StoredSettings::saveSwatchColours()
|
||||
{
|
||||
PropertiesFile& props = getGlobalProperties();
|
||||
|
||||
for (int i = 0; i < swatchColours.size(); ++i)
|
||||
props.setValue ("swatchColour" + String (i), swatchColours.getReference(i).toString());
|
||||
}
|
||||
|
||||
int StoredSettings::ColourSelectorWithSwatches::getNumSwatches() const
|
||||
{
|
||||
return getAppSettings().swatchColours.size();
|
||||
}
|
||||
|
||||
Colour StoredSettings::ColourSelectorWithSwatches::getSwatchColour (int index) const
|
||||
{
|
||||
return getAppSettings().swatchColours [index];
|
||||
}
|
||||
|
||||
void StoredSettings::ColourSelectorWithSwatches::setSwatchColour (int index, const Colour& newColour) const
|
||||
{
|
||||
getAppSettings().swatchColours.set (index, newColour);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_STOREDSETTINGS_JUCEHEADER__
|
||||
#define __JUCER_STOREDSETTINGS_JUCEHEADER__
|
||||
|
||||
#include "../Application/jucer_AppearanceSettings.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class StoredSettings
|
||||
{
|
||||
public:
|
||||
StoredSettings();
|
||||
~StoredSettings();
|
||||
|
||||
PropertiesFile& getGlobalProperties();
|
||||
PropertiesFile& getProjectProperties (const String& projectUID);
|
||||
|
||||
void flush();
|
||||
void reload();
|
||||
|
||||
//==============================================================================
|
||||
RecentlyOpenedFilesList recentFiles;
|
||||
|
||||
Array<File> getLastProjects();
|
||||
void setLastProjects (const Array<File>& files);
|
||||
|
||||
//==============================================================================
|
||||
Array<Colour> swatchColours;
|
||||
|
||||
class ColourSelectorWithSwatches : public ColourSelector
|
||||
{
|
||||
public:
|
||||
ColourSelectorWithSwatches() {}
|
||||
|
||||
int getNumSwatches() const override;
|
||||
Colour getSwatchColour (int index) const override;
|
||||
void setSwatchColour (int index, const Colour& newColour) const override;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AppearanceSettings appearance;
|
||||
|
||||
StringArray monospacedFontNames;
|
||||
|
||||
private:
|
||||
OwnedArray<PropertiesFile> propertyFiles;
|
||||
|
||||
void updateGlobalProps();
|
||||
void loadSwatchColours();
|
||||
void saveSwatchColours();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StoredSettings)
|
||||
};
|
||||
|
||||
StoredSettings& getAppSettings();
|
||||
PropertiesFile& getGlobalProperties();
|
||||
|
||||
|
||||
#endif // __JUCER_STOREDSETTINGS_JUCEHEADER__
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_TRANSLATIONTOOL_JUCEHEADER__
|
||||
#define __JUCER_TRANSLATIONTOOL_JUCEHEADER__
|
||||
|
||||
|
||||
struct TranslationHelpers
|
||||
{
|
||||
static void addString (StringArray& strings, const String& s)
|
||||
{
|
||||
if (s.isNotEmpty() && ! strings.contains (s))
|
||||
strings.add (s);
|
||||
}
|
||||
|
||||
static void scanFileForTranslations (StringArray& strings, const File& file)
|
||||
{
|
||||
const String content (file.loadFileAsString());
|
||||
|
||||
String::CharPointerType p (content.getCharPointer());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
p = CharacterFunctions::find (p, CharPointer_ASCII ("TRANS"));
|
||||
|
||||
if (p.isEmpty())
|
||||
break;
|
||||
|
||||
p += 5;
|
||||
p = p.findEndOfWhitespace();
|
||||
|
||||
if (*p == '(')
|
||||
{
|
||||
++p;
|
||||
MemoryOutputStream text;
|
||||
parseStringLiteral (p, text);
|
||||
|
||||
addString (strings, text.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parseStringLiteral (String::CharPointerType& p, MemoryOutputStream& out) noexcept
|
||||
{
|
||||
p = p.findEndOfWhitespace();
|
||||
|
||||
if (p.getAndAdvance() == '"')
|
||||
{
|
||||
String::CharPointerType start (p);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
juce_wchar c = *p;
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
out << String (start, p);
|
||||
++p;
|
||||
parseStringLiteral (p, out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == 0)
|
||||
break;
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
out << String (start, p);
|
||||
++p;
|
||||
out << String::charToString (readEscapedChar (p));
|
||||
start = p + 1;
|
||||
}
|
||||
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static juce_wchar readEscapedChar (String::CharPointerType& p)
|
||||
{
|
||||
juce_wchar c = *p;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/': break;
|
||||
|
||||
case 'b': c = '\b'; break;
|
||||
case 'f': c = '\f'; break;
|
||||
case 'n': c = '\n'; break;
|
||||
case 'r': c = '\r'; break;
|
||||
case 't': c = '\t'; break;
|
||||
|
||||
case 'x':
|
||||
++p;
|
||||
c = 0;
|
||||
|
||||
for (int i = 4; --i >= 0;)
|
||||
{
|
||||
const int digitValue = CharacterFunctions::getHexDigitValue (*p);
|
||||
if (digitValue < 0)
|
||||
break;
|
||||
|
||||
++p;
|
||||
c = (juce_wchar) ((c << 4) + digitValue);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
c = 0;
|
||||
|
||||
for (int i = 4; --i >= 0;)
|
||||
{
|
||||
const int digitValue = *p - '0';
|
||||
if (digitValue < 0 || digitValue > 7)
|
||||
break;
|
||||
|
||||
++p;
|
||||
c = (juce_wchar) ((c << 3) + digitValue);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static void scanFilesForTranslations (StringArray& strings, const Project::Item& p)
|
||||
{
|
||||
if (p.isFile())
|
||||
{
|
||||
const File file (p.getFile());
|
||||
|
||||
if (file.hasFileExtension (sourceOrHeaderFileExtensions))
|
||||
scanFileForTranslations (strings, file);
|
||||
}
|
||||
|
||||
for (int i = 0; i < p.getNumChildren(); ++i)
|
||||
scanFilesForTranslations (strings, p.getChild (i));
|
||||
}
|
||||
|
||||
static void scanProject (StringArray& strings, Project& project)
|
||||
{
|
||||
scanFilesForTranslations (strings, project.getMainGroup());
|
||||
|
||||
OwnedArray<LibraryModule> modules;
|
||||
project.getModules().createRequiredModules (modules);
|
||||
|
||||
for (int j = 0; j < modules.size(); ++j)
|
||||
{
|
||||
const File localFolder (modules.getUnchecked(j)->getFolder());
|
||||
|
||||
Array<File> files;
|
||||
modules.getUnchecked(j)->findBrowseableFiles (localFolder, files);
|
||||
|
||||
for (int i = 0; i < files.size(); ++i)
|
||||
scanFileForTranslations (strings, files.getReference(i));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* getMungingSeparator() { return "JCTRIDX"; }
|
||||
|
||||
static StringArray breakApart (const String& munged)
|
||||
{
|
||||
StringArray lines, result;
|
||||
lines.addLines (munged);
|
||||
|
||||
String currentItem;
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].contains (getMungingSeparator()))
|
||||
{
|
||||
if (currentItem.isNotEmpty())
|
||||
result.add (currentItem);
|
||||
|
||||
currentItem = String::empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentItem.isNotEmpty())
|
||||
currentItem << newLine;
|
||||
|
||||
currentItem << lines[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (currentItem.isNotEmpty())
|
||||
result.add (currentItem);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static String escapeString (const String& s)
|
||||
{
|
||||
return s.replace ("\"", "\\\"")
|
||||
.replace ("\'", "\\\'")
|
||||
.replace ("\t", "\\t")
|
||||
.replace ("\r", "\\r")
|
||||
.replace ("\n", "\\n");
|
||||
}
|
||||
|
||||
static String getPreTranslationText (Project& project)
|
||||
{
|
||||
StringArray strings;
|
||||
scanProject (strings, project);
|
||||
return mungeStrings (strings);
|
||||
}
|
||||
|
||||
static String getPreTranslationText (const LocalisedStrings& strings)
|
||||
{
|
||||
return mungeStrings (strings.getMappings().getAllKeys());
|
||||
}
|
||||
|
||||
static String mungeStrings (const StringArray& strings)
|
||||
{
|
||||
MemoryOutputStream s;
|
||||
|
||||
for (int i = 0; i < strings.size(); ++i)
|
||||
{
|
||||
s << getMungingSeparator() << i << "." << newLine << strings[i];
|
||||
|
||||
if (i < strings.size() - 1)
|
||||
s << newLine;
|
||||
}
|
||||
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
static String createLine (const String& preString, const String& postString)
|
||||
{
|
||||
return "\"" + escapeString (preString)
|
||||
+ "\" = \""
|
||||
+ escapeString (postString) + "\"";
|
||||
}
|
||||
|
||||
static String createFinishedTranslationFile (StringArray preStrings,
|
||||
StringArray postStrings,
|
||||
const LocalisedStrings& original)
|
||||
{
|
||||
const StringPairArray& originalStrings (original.getMappings());
|
||||
|
||||
StringArray lines;
|
||||
|
||||
if (originalStrings.size() > 0)
|
||||
{
|
||||
lines.add ("language: " + original.getLanguageName());
|
||||
lines.add ("countries: " + original.getCountryCodes().joinIntoString (" "));
|
||||
lines.add (String::empty);
|
||||
|
||||
const StringArray& originalKeys (originalStrings.getAllKeys());
|
||||
const StringArray& originalValues (originalStrings.getAllValues());
|
||||
int numRemoved = 0;
|
||||
|
||||
for (int i = preStrings.size(); --i >= 0;)
|
||||
{
|
||||
if (originalKeys.contains (preStrings[i]))
|
||||
{
|
||||
preStrings.remove (i);
|
||||
postStrings.remove (i);
|
||||
++numRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < originalStrings.size(); ++i)
|
||||
lines.add (createLine (originalKeys[i], originalValues[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
lines.add ("language: [enter full name of the language here!]");
|
||||
lines.add ("countries: [enter list of 2-character country codes here!]");
|
||||
lines.add (String::empty);
|
||||
}
|
||||
|
||||
for (int i = 0; i < preStrings.size(); ++i)
|
||||
lines.add (createLine (preStrings[i], postStrings[i]));
|
||||
|
||||
return lines.joinIntoString (newLine);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class TranslationToolComponent : public Component,
|
||||
public ButtonListener
|
||||
{
|
||||
public:
|
||||
TranslationToolComponent()
|
||||
: editorOriginal (documentOriginal, nullptr),
|
||||
editorPre (documentPre, nullptr),
|
||||
editorPost (documentPost, nullptr),
|
||||
editorResult (documentResult, nullptr)
|
||||
{
|
||||
setLookAndFeel (&lf);
|
||||
|
||||
instructionsLabel.setText (
|
||||
"This utility converts translation files to/from a format that can be passed to automatic translation tools."
|
||||
"\n\n"
|
||||
"First, choose whether to scan the current project for all TRANS() macros, or "
|
||||
"pick an existing translation file to load:", dontSendNotification);
|
||||
addAndMakeVisible (instructionsLabel);
|
||||
|
||||
label1.setText ("..then copy-and-paste this annotated text into Google Translate or some other translator:", dontSendNotification);
|
||||
addAndMakeVisible (label1);
|
||||
|
||||
label2.setText ("...then, take the translated result and paste it into the box below:", dontSendNotification);
|
||||
addAndMakeVisible (label2);
|
||||
|
||||
label3.setText ("Finally, click the 'Generate' button, and a translation file will be created below. "
|
||||
"Remember to update its language code at the top!", dontSendNotification);
|
||||
addAndMakeVisible (label3);
|
||||
|
||||
label4.setText ("If you load an existing file the already translated strings will be removed. Ensure this box is empty to create a fresh translation", dontSendNotification);
|
||||
addAndMakeVisible (label4);
|
||||
|
||||
addAndMakeVisible (editorOriginal);
|
||||
addAndMakeVisible (editorPre);
|
||||
addAndMakeVisible (editorPost);
|
||||
addAndMakeVisible (editorResult);
|
||||
|
||||
generateButton.setButtonText (TRANS("Generate"));
|
||||
addAndMakeVisible (generateButton);
|
||||
scanButton.setButtonText ("Scan Project for TRANS macros");
|
||||
addAndMakeVisible (scanButton);
|
||||
loadButton.setButtonText ("Load existing translation File...");
|
||||
addAndMakeVisible (loadButton);
|
||||
generateButton.addListener (this);
|
||||
|
||||
scanButton.addListener (this);
|
||||
loadButton.addListener (this);
|
||||
}
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
IntrojucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
||||
}
|
||||
|
||||
void resized()
|
||||
{
|
||||
const int m = 6;
|
||||
const int textH = 44;
|
||||
const int extraH = (7 * textH);
|
||||
const int editorH = (getHeight() - extraH) / 4;
|
||||
|
||||
Rectangle<int> r (getLocalBounds().withTrimmedBottom (m));
|
||||
instructionsLabel.setBounds (r.removeFromTop (textH * 2).reduced (m));
|
||||
r.removeFromTop (m);
|
||||
Rectangle<int> r2 (r.removeFromTop (textH - (2 * m)));
|
||||
scanButton.setBounds (r2.removeFromLeft (r.getWidth() / 2).reduced (m, 0));
|
||||
loadButton.setBounds (r2.reduced (m, 0));
|
||||
|
||||
label1.setBounds (r.removeFromTop (textH).reduced (m));
|
||||
editorPre.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
label2.setBounds (r.removeFromTop (textH).reduced (m));
|
||||
editorPost.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
r2 = r.removeFromTop (textH);
|
||||
generateButton.setBounds (r2.removeFromRight (152).reduced (m));
|
||||
label3.setBounds (r2.reduced (m));
|
||||
editorResult.setBounds (r.removeFromTop (editorH).reduced (m, 0));
|
||||
|
||||
label4.setBounds (r.removeFromTop (textH).reduced (m));
|
||||
editorOriginal.setBounds (r.reduced (m, 0));
|
||||
}
|
||||
|
||||
private:
|
||||
CodeDocument documentOriginal, documentPre, documentPost, documentResult;
|
||||
CodeEditorComponent editorOriginal, editorPre, editorPost, editorResult;
|
||||
juce::Label label1, label2, label3, label4;
|
||||
juce::TextButton generateButton;
|
||||
juce::Label instructionsLabel;
|
||||
juce::TextButton scanButton;
|
||||
juce::TextButton loadButton;
|
||||
|
||||
IntrojucerLookAndFeel lf;
|
||||
|
||||
void buttonClicked (Button* b)
|
||||
{
|
||||
if (b == &generateButton) generate();
|
||||
else if (b == &loadButton) loadFile();
|
||||
else if (b == &scanButton) scanProject();
|
||||
}
|
||||
|
||||
void generate()
|
||||
{
|
||||
StringArray preStrings (TranslationHelpers::breakApart (documentPre.getAllContent()));
|
||||
StringArray postStrings (TranslationHelpers::breakApart (documentPost.getAllContent()));
|
||||
|
||||
if (postStrings.size() != preStrings.size())
|
||||
{
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||
TRANS("Error"),
|
||||
TRANS("The pre- and post-translation text doesn't match!\n\n"
|
||||
"Perhaps it got mangled by the translator?"));
|
||||
return;
|
||||
}
|
||||
|
||||
const LocalisedStrings originalTranslation (documentOriginal.getAllContent(), false);
|
||||
documentResult.replaceAllContent (TranslationHelpers::createFinishedTranslationFile (preStrings, postStrings, originalTranslation));
|
||||
}
|
||||
|
||||
void loadFile()
|
||||
{
|
||||
FileChooser fc ("Choose a translation file to load",
|
||||
File::nonexistent,
|
||||
"*");
|
||||
|
||||
if (fc.browseForFileToOpen())
|
||||
{
|
||||
const LocalisedStrings loadedStrings (fc.getResult(), false);
|
||||
documentOriginal.replaceAllContent (fc.getResult().loadFileAsString().trim());
|
||||
setPreTranslationText (TranslationHelpers::getPreTranslationText (loadedStrings));
|
||||
}
|
||||
}
|
||||
|
||||
void scanProject()
|
||||
{
|
||||
if (Project* project = IntrojucerApp::getApp().mainWindowList.getFrontmostProject())
|
||||
setPreTranslationText (TranslationHelpers::getPreTranslationText (*project));
|
||||
else
|
||||
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Translation Tool",
|
||||
"This will only work when you have a project open!");
|
||||
}
|
||||
|
||||
void setPreTranslationText (const String& text)
|
||||
{
|
||||
documentPre.replaceAllContent (text);
|
||||
editorPre.grabKeyboardFocus();
|
||||
editorPre.selectAll();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_TRANSLATIONTOOL_JUCEHEADER__
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef __JUCER_VALUESOURCEHELPERS_JUCEHEADER__
|
||||
#define __JUCER_VALUESOURCEHELPERS_JUCEHEADER__
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
template <typename Type>
|
||||
class NumericValueSource : public ValueSourceFilter
|
||||
{
|
||||
public:
|
||||
NumericValueSource (const Value& source) : ValueSourceFilter (source) {}
|
||||
|
||||
var getValue() const override
|
||||
{
|
||||
return (Type) sourceValue.getValue();
|
||||
}
|
||||
|
||||
void setValue (const var& newValue) override
|
||||
{
|
||||
const Type newVal = static_cast <Type> (newValue);
|
||||
|
||||
if (newVal != static_cast <Type> (getValue())) // this test is important, because if a property is missing, it won't
|
||||
sourceValue = newVal; // create it (causing an unwanted undo action) when a control sets it to 0
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NumericValueSource)
|
||||
};
|
||||
|
||||
|
||||
#endif // __JUCER_VALUESOURCEHELPERS_JUCEHEADER__
|
||||
Reference in New Issue
Block a user