1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-27 02:20:05 +00:00
JUCE/extras/the jucer/src/utility/jucer_UtilityFunctions.cpp

760 lines
26 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "../jucer_Headers.h"
#include "../model/jucer_JucerDocument.h"
//==============================================================================
const String replaceCEscapeChars (const String& s)
{
const int len = s.length();
String r;
r.preallocateStorage (len + 2);
bool lastWasHexEscapeCode = false;
for (int i = 0; i < len; ++i)
{
const tchar c = s[i];
switch (c)
{
case '\t':
r << T("\\t");
lastWasHexEscapeCode = false;
break;
case '\r':
r << T("\\r");
lastWasHexEscapeCode = false;
break;
case '\n':
r << T("\\n");
lastWasHexEscapeCode = false;
break;
case '\\':
r << T("\\\\");
lastWasHexEscapeCode = false;
break;
case '\'':
r << T("\\\'");
lastWasHexEscapeCode = false;
break;
case '\"':
r << T("\\\"");
lastWasHexEscapeCode = false;
break;
default:
if (c < 128 &&
! (lastWasHexEscapeCode
&& String (T("0123456789abcdefABCDEF")).containsChar (c))) // (have to avoid following a hex escape sequence with a valid hex digit)
{
r << c;
lastWasHexEscapeCode = false;
}
else
{
lastWasHexEscapeCode = true;
r << T("\\x") << String::toHexString ((int) c);
}
break;
}
}
return r;
}
const String quotedString (const String& s)
{
if (s.isEmpty())
return T("String::empty");
const int embeddedIndex = s.indexOfIgnoreCase (T("%%"));
if (embeddedIndex >= 0)
{
String s1 (s.substring (0, embeddedIndex));
String s2 (s.substring (embeddedIndex + 2));
String code;
const int closeIndex = s2.indexOf (T("%%"));
if (closeIndex > 0)
{
code = s2.substring (0, closeIndex).trim();
s2 = s2.substring (closeIndex + 2);
}
if (code.isNotEmpty())
{
String result;
if (s1.isNotEmpty())
result << quotedString (s1) << T(" + ");
result << code;
if (s2.isNotEmpty())
result << T(" + ") << quotedString (s2);
return result;
}
}
return T("T(\"") + replaceCEscapeChars (s) + T("\")");
}
const String replaceStringTranslations (String s, JucerDocument* document)
{
s = s.replace (T("%%getName()%%"), document->getComponentName());
s = s.replace (T("%%getButtonText()%%"), document->getComponentName());
return s;
}
const String castToFloat (const String& expression)
{
if (expression.containsOnly (T("0123456789.f")))
{
String s (expression.getFloatValue());
if (s.containsChar (T('.')))
return s + T("f");
return s + T(".0f");
}
return T("(float) (") + expression + T(")");
}
const String indentCode (const String& code, const int numSpaces)
{
if (numSpaces == 0)
return code;
const String space (String::repeatedString (T(" "), numSpaces));
StringArray lines;
lines.addLines (code);
for (int i = 1; i < lines.size(); ++i)
{
String s (lines[i].trimEnd());
if (s.isNotEmpty())
s = space + s;
lines.set (i, s);
}
return lines.joinIntoString (T("\n"));
}
//==============================================================================
const String makeValidCppIdentifier (String s,
const bool capitalise,
const bool removeColons,
const bool allowTemplates)
{
if (removeColons)
s = s.replaceCharacters (T(".,;:/@"), T("______"));
else
s = s.replaceCharacters (T(".,;/@"), T("_____"));
int i;
for (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) + T(" ") + s.substring (i);
String allowedChars (T("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789"));
if (allowTemplates)
allowedChars += T("<>");
if (! removeColons)
allowedChars += T(":");
StringArray words;
words.addTokens (s.retainCharacters (allowedChars), false);
words.trim();
String n (words[0]);
if (capitalise)
n = n.toLowerCase();
for (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 = T("_") + n;
// make sure it's not a reserved c++ keyword..
static const tchar* const reservedWords[] =
{
T("auto"), T("const"), T("double"), T("float"), T("int"), T("short"), T("struct"),
T("return"), T("static"), T("union"), T("while"), T("asm"), T("dynamic_cast"),
T("unsigned"), T("break"), T("continue"), T("else"), T("for"), T("long"), T("signed"),
T("switch"), T("void"), T("case"), T("default"), T("enum"), T("goto"), T("register"),
T("sizeof"), T("typedef"), T("volatile"), T("char"), T("do"), T("extern"), T("if"),
T("namespace"), T("reinterpret_cast"), T("try"), T("bool"), T("explicit"), T("new"),
T("static_cast"), T("typeid"), T("catch"), T("false"), T("operator"), T("template"),
T("typename"), T("class"), T("friend"), T("private"), T("this"), T("using"), T("const_cast"),
T("inline"), T("public"), T("throw"), T("virtual"), T("delete"), T("mutable"), T("protected"),
T("true"), T("wchar_t"), T("and"), T("bitand"), T("compl"), T("not_eq"), T("or_eq"),
T("xor_eq"), T("and_eq"), T("bitor"), T("not"), T("or"), T("xor"), T("cin"), T("endl"),
T("INT_MIN"), T("iomanip"), T("main"), T("npos"), T("std"), T("cout"), T("include"),
T("INT_MAX"), T("iostream"), T("MAX_RAND"), T("NULL"), T("string")
};
for (i = 0; i < numElementsInArray (reservedWords); ++i)
if (n == reservedWords[i])
n << '_';
return n;
}
//==============================================================================
int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex)
{
startIndex = jmax (0, startIndex);
while (startIndex < lines.size())
{
if (lines[startIndex].trimStart().startsWithIgnoreCase (text))
return startIndex;
++startIndex;
}
return -1;
}
//==============================================================================
const String valueToFloat (const double v)
{
String s ((double) (float) v, 4);
if (s.containsChar (T('.')))
s << 'f';
else
s << ".0f";
return s;
}
const String boolToString (const bool b)
{
return b ? T("true") : T("false");
}
//==============================================================================
const String colourToHex (const Colour& col)
{
return String::toHexString ((int) col.getARGB());
}
//==============================================================================
const String colourToCode (const Colour& col)
{
#define COL(col) Colours::col,
const Colour colours[] =
{
#include "jucer_Colours.h"
Colours::transparentBlack
};
#undef COL
#define COL(col) #col,
static const char* colourNames[] =
{
#include "jucer_Colours.h"
0
};
#undef COL
for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
if (col == colours[i])
return T("Colours::") + String (colourNames[i]);
return T("Colour (0x") + colourToHex (col) + T(')');
}
void setColourXml (XmlElement& xml, const tchar* const attName, const Colour& colour)
{
xml.setAttribute (attName, colourToHex (colour));
}
const Colour getColourXml (const XmlElement& xml, const tchar* const attName, const Colour& defaultColour)
{
return Colour (xml.getStringAttribute (attName, colourToHex (defaultColour)).getHexValue32());
}
//==============================================================================
const String positionToString (const RelativePositionedRectangle& pos)
{
StringArray toks;
toks.addTokens (pos.rect.toString(), false);
return toks[0] + T(" ") + toks[1];
}
void positionToXY (const RelativePositionedRectangle& position,
double& x, double& y,
const Rectangle<int>& parentArea,
const ComponentLayout* layout)
{
double w, h;
position.getRectangleDouble (x, y, w, h, parentArea, layout);
}
void positionToCode (const RelativePositionedRectangle& position,
const ComponentLayout* layout,
String& x, String& y, String& w, String& h)
{
// these are the code sections for the positions of the relative comps
String xrx, xry, xrw, xrh;
Component* const relCompX = layout != 0 ? layout->findComponentWithId (position.relativeToX) : 0;
if (relCompX != 0)
positionToCode (ComponentTypeHandler::getComponentPosition (relCompX), layout, xrx, xry, xrw, xrh);
String yrx, yry, yrw, yrh;
Component* const relCompY = layout != 0 ? layout->findComponentWithId (position.relativeToY) : 0;
if (relCompY != 0)
positionToCode (ComponentTypeHandler::getComponentPosition (relCompY), layout, yrx, yry, yrw, yrh);
String wrx, wry, wrw, wrh;
Component* const relCompW = (layout != 0 && position.rect.getWidthMode() != PositionedRectangle::absoluteSize)
? layout->findComponentWithId (position.relativeToW) : 0;
if (relCompW != 0)
positionToCode (ComponentTypeHandler::getComponentPosition (relCompW), layout, wrx, wry, wrw, wrh);
String hrx, hry, hrw, hrh;
Component* const relCompH = (layout != 0 && position.rect.getHeightMode() != PositionedRectangle::absoluteSize)
? layout->findComponentWithId (position.relativeToH) : 0;
if (relCompH != 0)
positionToCode (ComponentTypeHandler::getComponentPosition (relCompH), layout, hrx, hry, hrw, hrh);
// width
if (position.rect.getWidthMode() == PositionedRectangle::proportionalSize)
{
if (wrw.isNotEmpty())
w << "roundFloatToInt ((" << wrw << ") * " << valueToFloat (position.rect.getWidth()) << ")";
else
w << "proportionOfWidth (" << valueToFloat (position.rect.getWidth()) << ")";
}
else if (position.rect.getWidthMode() == PositionedRectangle::parentSizeMinusAbsolute)
{
if (wrw.isNotEmpty())
w << "(" << wrw << ") - " << roundToInt (position.rect.getWidth());
else
w << "getWidth() - " << roundToInt (position.rect.getWidth());
}
else
{
if (wrw.isNotEmpty())
w << "(" << wrw << ") + ";
w << roundToInt (position.rect.getWidth());
}
// height
if (position.rect.getHeightMode() == PositionedRectangle::proportionalSize)
{
if (hrh.isNotEmpty())
h << "roundFloatToInt ((" << hrh << ") * " << valueToFloat (position.rect.getHeight()) << ")";
else
h << "proportionOfHeight (" << valueToFloat (position.rect.getHeight()) << ")";
}
else if (position.rect.getHeightMode() == PositionedRectangle::parentSizeMinusAbsolute)
{
if (hrh.isNotEmpty())
h << "(" << hrh << ") - " << roundToInt (position.rect.getHeight());
else
h << "getHeight() - " << roundToInt (position.rect.getHeight());
}
else
{
if (hrh.isNotEmpty())
h << "(" << hrh << ") + ";
h << roundToInt (position.rect.getHeight());
}
// x-pos
if (position.rect.getPositionModeX() == PositionedRectangle::proportionOfParentSize)
{
if (xrx.isNotEmpty() && xrw.isNotEmpty())
x << "(" << xrx << ") + roundFloatToInt ((" << xrw << ") * " << valueToFloat (position.rect.getX()) << ")";
else
x << "proportionOfWidth (" << valueToFloat (position.rect.getX()) << ")";
}
else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentTopLeft)
{
if (xrx.isNotEmpty())
x << "(" << xrx << ") + ";
x << roundToInt (position.rect.getX());
}
else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentBottomRight)
{
if (xrx.isNotEmpty())
x << "(" << xrx << ") + (" << xrw << ")";
else
x << "getWidth()";
const int d = roundToInt (position.rect.getX());
if (d != 0)
x << " - " << d;
}
else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentCentre)
{
if (xrx.isNotEmpty())
x << "(" << xrx << ") + (" << xrw << ") / 2";
else
x << "(getWidth() / 2)";
const int d = roundToInt (position.rect.getX());
if (d != 0)
x << " + " << d;
}
if (w != T("0"))
{
if (position.rect.getAnchorPointX() == PositionedRectangle::anchorAtRightOrBottom)
x << " - " << w;
else if (position.rect.getAnchorPointX() == PositionedRectangle::anchorAtCentre)
x << " - ((" << w << ") / 2)";
}
// y-pos
if (position.rect.getPositionModeY() == PositionedRectangle::proportionOfParentSize)
{
if (yry.isNotEmpty() && yrh.isNotEmpty())
y << "(" << yry << ") + roundFloatToInt ((" << yrh << ") * " << valueToFloat (position.rect.getY()) << ")";
else
y << "proportionOfHeight (" << valueToFloat (position.rect.getY()) << ")";
}
else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentTopLeft)
{
if (yry.isNotEmpty())
y << "(" << yry << ") + ";
y << roundToInt (position.rect.getY());
}
else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentBottomRight)
{
if (yry.isNotEmpty())
y << "(" << yry << ") + (" << yrh << ")";
else
y << "getHeight()";
const int d = roundToInt (position.rect.getY());
if (d != 0)
y << " - " << d;
}
else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentCentre)
{
if (yry.isNotEmpty())
y << "(" << yry << ") + (" << yrh << ") / 2";
else
y << "(getHeight() / 2)";
const int d = roundToInt (position.rect.getY());
if (d != 0)
y << " + " << d;
}
if (h != T("0"))
{
if (position.rect.getAnchorPointY() == PositionedRectangle::anchorAtRightOrBottom)
y << " - " << h;
else if (position.rect.getAnchorPointY() == PositionedRectangle::anchorAtCentre)
y << " - ((" << h << ") / 2)";
}
}
const String justificationToCode (const 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:
jassertfalse
break;
}
return T("Justification (") + String (justification.getFlags()) + T(")");
}
//==============================================================================
void drawResizableBorder (Graphics& g,
int w, int h,
const BorderSize borderSize,
const bool isMouseOver)
{
g.setColour (Colours::orange.withAlpha (isMouseOver ? 0.4f : 0.3f));
g.fillRect (0, 0, w, borderSize.getTop());
g.fillRect (0, 0, borderSize.getLeft(), h);
g.fillRect (0, h - borderSize.getBottom(), w, borderSize.getBottom());
g.fillRect (w - borderSize.getRight(), 0, borderSize.getRight(), h);
g.drawRect (borderSize.getLeft() - 1, borderSize.getTop() - 1,
w - borderSize.getRight() - borderSize.getLeft() + 2,
h - borderSize.getTop() - borderSize.getBottom() + 2);
}
void drawMouseOverCorners (Graphics& g, int w, int h)
{
RectangleList r (Rectangle<int> (0, 0, w, h));
r.subtract (Rectangle<int> (1, 1, w - 2, h - 2));
const int size = jmin (w / 3, h / 3, 12);
r.subtract (Rectangle<int> (size, 0, w - size - size, h));
r.subtract (Rectangle<int> (0, size, w, h - size - size));
g.setColour (Colours::darkgrey);
for (int i = r.getNumRectangles(); --i >= 0;)
g.fillRect (r.getRectangle (i));
}
//==============================================================================
RelativePositionedRectangle::RelativePositionedRectangle()
: relativeToX (0),
relativeToY (0),
relativeToW (0),
relativeToH (0)
{
}
RelativePositionedRectangle::RelativePositionedRectangle (const RelativePositionedRectangle& other)
: rect (other.rect),
relativeToX (other.relativeToX),
relativeToY (other.relativeToY),
relativeToW (other.relativeToW),
relativeToH (other.relativeToH)
{
}
const RelativePositionedRectangle& RelativePositionedRectangle::operator= (const RelativePositionedRectangle& other)
{
rect = other.rect;
relativeToX = other.relativeToX;
relativeToY = other.relativeToY;
relativeToW = other.relativeToW;
relativeToH = other.relativeToH;
return *this;
}
RelativePositionedRectangle::~RelativePositionedRectangle()
{
}
//==============================================================================
bool RelativePositionedRectangle::operator== (const RelativePositionedRectangle& other) const throw()
{
return rect == other.rect
&& relativeToX == other.relativeToX
&& relativeToY == other.relativeToY
&& relativeToW == other.relativeToW
&& relativeToH == other.relativeToH;
}
bool RelativePositionedRectangle::operator!= (const RelativePositionedRectangle& other) const throw()
{
return ! operator== (other);
}
void RelativePositionedRectangle::getRelativeTargetBounds (const Rectangle<int>& parentArea,
const ComponentLayout* layout,
int& x, int& xw, int& y, int& yh,
int& w, int& h) const
{
Component* rx = 0;
Component* ry = 0;
Component* rw = 0;
Component* rh = 0;
if (layout != 0)
{
rx = layout->findComponentWithId (relativeToX);
ry = layout->findComponentWithId (relativeToY);
rw = layout->findComponentWithId (relativeToW);
rh = layout->findComponentWithId (relativeToH);
}
x = parentArea.getX() + (rx != 0 ? rx->getX() : 0);
y = parentArea.getY() + (ry != 0 ? ry->getY() : 0);
w = rw != 0 ? rw->getWidth() : parentArea.getWidth();
h = rh != 0 ? rh->getHeight() : parentArea.getHeight();
xw = rx != 0 ? rx->getWidth() : parentArea.getWidth();
yh = ry != 0 ? ry->getHeight() : parentArea.getHeight();
}
const Rectangle<int> RelativePositionedRectangle::getRectangle (const Rectangle<int>& parentArea,
const ComponentLayout* layout) const
{
int x, xw, y, yh, w, h;
getRelativeTargetBounds (parentArea, layout, x, xw, y, yh, w, h);
const Rectangle<int> xyRect ((xw <= 0 || yh <= 0) ? Rectangle<int>()
: rect.getRectangle (Rectangle<int> (x, y, xw, yh)));
const Rectangle<int> whRect ((w <= 0 || h <= 0) ? Rectangle<int>()
: rect.getRectangle (Rectangle<int> (x, y, w, h)));
return Rectangle<int> (xyRect.getX(), xyRect.getY(),
whRect.getWidth(), whRect.getHeight());
}
void RelativePositionedRectangle::getRectangleDouble (double& x, double& y, double& w, double& h,
const Rectangle<int>& parentArea,
const ComponentLayout* layout) const
{
int rx, rxw, ry, ryh, rw, rh;
getRelativeTargetBounds (parentArea, layout, rx, rxw, ry, ryh, rw, rh);
double dummy1, dummy2;
rect.getRectangleDouble (Rectangle<int> (rx, ry, rxw, ryh), x, y, dummy1, dummy2);
rect.getRectangleDouble (Rectangle<int> (rx, ry, rw, rh), dummy1, dummy2, w, h);
}
void RelativePositionedRectangle::updateFromComponent (const Component& comp,
const ComponentLayout* layout)
{
int x, xw, y, yh, w, h;
getRelativeTargetBounds (Rectangle<int> (0, 0, comp.getParentWidth(), comp.getParentHeight()),
layout, x, xw, y, yh, w, h);
PositionedRectangle xyRect (rect), whRect (rect);
xyRect.updateFrom (comp.getBounds(), Rectangle<int> (x, y, xw, yh));
whRect.updateFrom (comp.getBounds(), Rectangle<int> (x, y, w, h));
rect.setX (xyRect.getX());
rect.setY (xyRect.getY());
rect.setWidth (whRect.getWidth());
rect.setHeight (whRect.getHeight());
}
void RelativePositionedRectangle::updateFrom (double newX, double newY, double newW, double newH,
const Rectangle<int>& parentArea, const ComponentLayout* layout)
{
int x, xw, y, yh, w, h;
getRelativeTargetBounds (parentArea, layout, x, xw, y, yh, w, h);
PositionedRectangle xyRect (rect), whRect (rect);
xyRect.updateFromDouble (newX, newY, newW, newH, Rectangle<int> (x, y, xw, yh));
whRect.updateFromDouble (newX, newY, newW, newH, Rectangle<int> (x, y, w, h));
rect.setX (xyRect.getX());
rect.setY (xyRect.getY());
rect.setWidth (whRect.getWidth());
rect.setHeight (whRect.getHeight());
}
void RelativePositionedRectangle::applyToXml (XmlElement& e) const
{
e.setAttribute (T("pos"), rect.toString());
if (relativeToX != 0)
e.setAttribute (T("posRelativeX"), String::toHexString (relativeToX));
if (relativeToY != 0)
e.setAttribute (T("posRelativeY"), String::toHexString (relativeToY));
if (relativeToW != 0)
e.setAttribute (T("posRelativeW"), String::toHexString (relativeToW));
if (relativeToH != 0)
e.setAttribute (T("posRelativeH"), String::toHexString (relativeToH));
}
void RelativePositionedRectangle::restoreFromXml (const XmlElement& e,
const RelativePositionedRectangle& defaultPos)
{
rect = PositionedRectangle (e.getStringAttribute (T("pos"), defaultPos.rect.toString()));
relativeToX = e.getStringAttribute (T("posRelativeX"), String::toHexString (defaultPos.relativeToX)).getHexValue64();
relativeToY = e.getStringAttribute (T("posRelativeY"), String::toHexString (defaultPos.relativeToY)).getHexValue64();
relativeToW = e.getStringAttribute (T("posRelativeW"), String::toHexString (defaultPos.relativeToW)).getHexValue64();
relativeToH = e.getStringAttribute (T("posRelativeH"), String::toHexString (defaultPos.relativeToH)).getHexValue64();
}