@@ -47,6 +47,7 @@ OBJECTS := \ | |||||
$(OBJDIR)/jucer_ComponentDocument.o \ | $(OBJDIR)/jucer_ComponentDocument.o \ | ||||
$(OBJDIR)/jucer_ComponentTypeManager.o \ | $(OBJDIR)/jucer_ComponentTypeManager.o \ | ||||
$(OBJDIR)/jucer_DrawableDocument.o \ | $(OBJDIR)/jucer_DrawableDocument.o \ | ||||
$(OBJDIR)/jucer_DrawableTypeHandler.o \ | |||||
$(OBJDIR)/jucer_NewFileWizard.o \ | $(OBJDIR)/jucer_NewFileWizard.o \ | ||||
$(OBJDIR)/jucer_Project.o \ | $(OBJDIR)/jucer_Project.o \ | ||||
$(OBJDIR)/jucer_ProjectExporter.o \ | $(OBJDIR)/jucer_ProjectExporter.o \ | ||||
@@ -113,6 +114,11 @@ $(OBJDIR)/jucer_DrawableDocument.o: ../../Source/model/Drawable/jucer_DrawableDo | |||||
@echo "Compiling jucer_DrawableDocument.cpp" | @echo "Compiling jucer_DrawableDocument.cpp" | ||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | ||||
$(OBJDIR)/jucer_DrawableTypeHandler.o: ../../Source/model/Drawable/jucer_DrawableTypeHandler.cpp | |||||
-@mkdir -p $(OBJDIR) | |||||
@echo "Compiling jucer_DrawableTypeHandler.cpp" | |||||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||||
$(OBJDIR)/jucer_NewFileWizard.o: ../../Source/model/Project/jucer_NewFileWizard.cpp | $(OBJDIR)/jucer_NewFileWizard.o: ../../Source/model/Project/jucer_NewFileWizard.cpp | ||||
-@mkdir -p $(OBJDIR) | -@mkdir -p $(OBJDIR) | ||||
@echo "Compiling jucer_NewFileWizard.cpp" | @echo "Compiling jucer_NewFileWizard.cpp" | ||||
@@ -21,6 +21,7 @@ | |||||
061C981D0E9FB70DE5A3D32C = { isa = PBXBuildFile; fileRef = 829C5DA65FB46B99756428C5; }; | 061C981D0E9FB70DE5A3D32C = { isa = PBXBuildFile; fileRef = 829C5DA65FB46B99756428C5; }; | ||||
FEDBCD721085272D356BBAEB = { isa = PBXBuildFile; fileRef = D08D9DB99315F76093CF6EE6; }; | FEDBCD721085272D356BBAEB = { isa = PBXBuildFile; fileRef = D08D9DB99315F76093CF6EE6; }; | ||||
268807D7091702D29033CC27 = { isa = PBXBuildFile; fileRef = B1471E8698D193FBCF0DD13D; }; | 268807D7091702D29033CC27 = { isa = PBXBuildFile; fileRef = B1471E8698D193FBCF0DD13D; }; | ||||
91CCD0421DDD432C1C4903D4 = { isa = PBXBuildFile; fileRef = 330A51CC68AED92F7F5BEC15; }; | |||||
06838545183964D8C1E60530 = { isa = PBXBuildFile; fileRef = 9DCB32BBD7053ACCB598CE79; }; | 06838545183964D8C1E60530 = { isa = PBXBuildFile; fileRef = 9DCB32BBD7053ACCB598CE79; }; | ||||
048D33BDE7413EFE05D09901 = { isa = PBXBuildFile; fileRef = 4179D4C7BF616A4A3C3E11CA; }; | 048D33BDE7413EFE05D09901 = { isa = PBXBuildFile; fileRef = 4179D4C7BF616A4A3C3E11CA; }; | ||||
9DA1913A62297D7111E0A6C9 = { isa = PBXBuildFile; fileRef = 48A4236550741B9D05208C60; }; | 9DA1913A62297D7111E0A6C9 = { isa = PBXBuildFile; fileRef = 48A4236550741B9D05208C60; }; | ||||
@@ -84,6 +85,7 @@ | |||||
E894E1F6D582678EE1F02763 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_Viewport.h; path = ../../Source/model/Component/Types/jucer_Viewport.h; sourceTree = SOURCE_ROOT; }; | E894E1F6D582678EE1F02763 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_Viewport.h; path = ../../Source/model/Component/Types/jucer_Viewport.h; sourceTree = SOURCE_ROOT; }; | ||||
B1471E8698D193FBCF0DD13D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_DrawableDocument.cpp; path = ../../Source/model/Drawable/jucer_DrawableDocument.cpp; sourceTree = SOURCE_ROOT; }; | B1471E8698D193FBCF0DD13D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_DrawableDocument.cpp; path = ../../Source/model/Drawable/jucer_DrawableDocument.cpp; sourceTree = SOURCE_ROOT; }; | ||||
739F94CA6213B43D867AB0FD = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableDocument.h; path = ../../Source/model/Drawable/jucer_DrawableDocument.h; sourceTree = SOURCE_ROOT; }; | 739F94CA6213B43D867AB0FD = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableDocument.h; path = ../../Source/model/Drawable/jucer_DrawableDocument.h; sourceTree = SOURCE_ROOT; }; | ||||
330A51CC68AED92F7F5BEC15 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_DrawableTypeHandler.cpp; path = ../../Source/model/Drawable/jucer_DrawableTypeHandler.cpp; sourceTree = SOURCE_ROOT; }; | |||||
BDE8CD9273E1B0D0E500D283 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableTypeHandler.h; path = ../../Source/model/Drawable/jucer_DrawableTypeHandler.h; sourceTree = SOURCE_ROOT; }; | BDE8CD9273E1B0D0E500D283 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_DrawableTypeHandler.h; path = ../../Source/model/Drawable/jucer_DrawableTypeHandler.h; sourceTree = SOURCE_ROOT; }; | ||||
9DCB32BBD7053ACCB598CE79 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_NewFileWizard.cpp; path = ../../Source/model/Project/jucer_NewFileWizard.cpp; sourceTree = SOURCE_ROOT; }; | 9DCB32BBD7053ACCB598CE79 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = jucer_NewFileWizard.cpp; path = ../../Source/model/Project/jucer_NewFileWizard.cpp; sourceTree = SOURCE_ROOT; }; | ||||
201DF0B7B4AA3E03B1AA5144 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_NewFileWizard.h; path = ../../Source/model/Project/jucer_NewFileWizard.h; sourceTree = SOURCE_ROOT; }; | 201DF0B7B4AA3E03B1AA5144 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jucer_NewFileWizard.h; path = ../../Source/model/Project/jucer_NewFileWizard.h; sourceTree = SOURCE_ROOT; }; | ||||
@@ -204,6 +206,7 @@ | |||||
E70E3BE796E91EA86CFE10FE = { isa = PBXGroup; children = ( | E70E3BE796E91EA86CFE10FE = { isa = PBXGroup; children = ( | ||||
B1471E8698D193FBCF0DD13D, | B1471E8698D193FBCF0DD13D, | ||||
739F94CA6213B43D867AB0FD, | 739F94CA6213B43D867AB0FD, | ||||
330A51CC68AED92F7F5BEC15, | |||||
BDE8CD9273E1B0D0E500D283 ); name = Drawable; sourceTree = "<group>"; }; | BDE8CD9273E1B0D0E500D283 ); name = Drawable; sourceTree = "<group>"; }; | ||||
ADA17383E5554BCDF567AACC = { isa = PBXGroup; children = ( | ADA17383E5554BCDF567AACC = { isa = PBXGroup; children = ( | ||||
9DCB32BBD7053ACCB598CE79, | 9DCB32BBD7053ACCB598CE79, | ||||
@@ -416,6 +419,7 @@ | |||||
061C981D0E9FB70DE5A3D32C, | 061C981D0E9FB70DE5A3D32C, | ||||
FEDBCD721085272D356BBAEB, | FEDBCD721085272D356BBAEB, | ||||
268807D7091702D29033CC27, | 268807D7091702D29033CC27, | ||||
91CCD0421DDD432C1C4903D4, | |||||
06838545183964D8C1E60530, | 06838545183964D8C1E60530, | ||||
048D33BDE7413EFE05D09901, | 048D33BDE7413EFE05D09901, | ||||
9DA1913A62297D7111E0A6C9, | 9DA1913A62297D7111E0A6C9, | ||||
@@ -154,6 +154,7 @@ | |||||
<Filter Name="Drawable"> | <Filter Name="Drawable"> | ||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.cpp"/> | <File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.cpp"/> | ||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.h"/> | <File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.h"/> | ||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.cpp"/> | |||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.h"/> | <File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.h"/> | ||||
</Filter> | </Filter> | ||||
<Filter Name="Project"> | <Filter Name="Project"> | ||||
@@ -154,6 +154,7 @@ | |||||
<Filter Name="Drawable"> | <Filter Name="Drawable"> | ||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.cpp"/> | <File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.cpp"/> | ||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.h"/> | <File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.h"/> | ||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.cpp"/> | |||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.h"/> | <File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.h"/> | ||||
</Filter> | </Filter> | ||||
<Filter Name="Project"> | <Filter Name="Project"> | ||||
@@ -72,6 +72,8 @@ | |||||
file="Source/model/Drawable/jucer_DrawableDocument.cpp"/> | file="Source/model/Drawable/jucer_DrawableDocument.cpp"/> | ||||
<FILE id="qMcrWuCal" name="jucer_DrawableDocument.h" compile="0" resource="0" | <FILE id="qMcrWuCal" name="jucer_DrawableDocument.h" compile="0" resource="0" | ||||
file="Source/model/Drawable/jucer_DrawableDocument.h"/> | file="Source/model/Drawable/jucer_DrawableDocument.h"/> | ||||
<FILE id="9y0VHaD" name="jucer_DrawableTypeHandler.cpp" compile="1" | |||||
resource="0" file="Source/model/Drawable/jucer_DrawableTypeHandler.cpp"/> | |||||
<FILE id="tGjXOXiR9" name="jucer_DrawableTypeHandler.h" compile="0" | <FILE id="tGjXOXiR9" name="jucer_DrawableTypeHandler.h" compile="0" | ||||
resource="0" file="Source/model/Drawable/jucer_DrawableTypeHandler.h"/> | resource="0" file="Source/model/Drawable/jucer_DrawableTypeHandler.h"/> | ||||
</GROUP> | </GROUP> | ||||
@@ -134,6 +134,7 @@ namespace Ids | |||||
DECLARE_ID (resource); | DECLARE_ID (resource); | ||||
DECLARE_ID (className); | DECLARE_ID (className); | ||||
DECLARE_ID (classDesc); | DECLARE_ID (classDesc); | ||||
DECLARE_ID (controlPoint); | |||||
const Identifier class_ ("class"); | const Identifier class_ ("class"); | ||||
const Identifier id_ ("id"); | const Identifier id_ ("id"); | ||||
} | } | ||||
@@ -27,80 +27,6 @@ | |||||
#include "jucer_DrawableTypeHandler.h" | #include "jucer_DrawableTypeHandler.h" | ||||
//============================================================================== | |||||
class DrawableTypeManager : public DeletedAtShutdown | |||||
{ | |||||
public: | |||||
DrawableTypeManager() | |||||
{ | |||||
handlers.add (new DrawablePathHandler()); | |||||
handlers.add (new DrawableImageHandler()); | |||||
handlers.add (new DrawableCompositeHandler()); | |||||
} | |||||
~DrawableTypeManager() | |||||
{ | |||||
} | |||||
juce_DeclareSingleton_SingleThreaded_Minimal (DrawableTypeManager); | |||||
//============================================================================== | |||||
int getNumHandlers() const { return handlers.size(); } | |||||
DrawableTypeHandler* getHandler (const int index) const { return handlers[index]; } | |||||
DrawableTypeHandler* getHandlerFor (const Identifier& type) | |||||
{ | |||||
for (int i = handlers.size(); --i >= 0;) | |||||
if (handlers.getUnchecked(i)->getValueTreeType() == type) | |||||
return handlers.getUnchecked(i); | |||||
jassertfalse; | |||||
return 0; | |||||
} | |||||
private: | |||||
OwnedArray <DrawableTypeHandler> handlers; | |||||
}; | |||||
juce_ImplementSingleton_SingleThreaded (DrawableTypeManager); | |||||
//============================================================================== | |||||
DrawableTypeInstance::DrawableTypeInstance (DrawableDocument& document_, const ValueTree& state_) | |||||
: document (document_), state (state_) | |||||
{ | |||||
} | |||||
Value DrawableTypeInstance::getValue (const Identifier& name) const | |||||
{ | |||||
return state.getPropertyAsValue (name, document.getUndoManager()); | |||||
} | |||||
void DrawableTypeInstance::createProperties (Array <PropertyComponent*>& props) | |||||
{ | |||||
props.add (new TextPropertyComponent (getValue (Drawable::ValueTreeWrapperBase::idProperty), "Object ID", 128, false)); | |||||
getHandler()->createPropertyEditors (*this, props); | |||||
} | |||||
DrawableTypeHandler* DrawableTypeInstance::getHandler() const | |||||
{ | |||||
DrawableTypeHandler* h = DrawableTypeManager::getInstance()->getHandlerFor (state.getType()); | |||||
jassert (h != 0); | |||||
return h; | |||||
} | |||||
void DrawableTypeInstance::setBounds (Drawable* drawable, const Rectangle<float>& newBounds) | |||||
{ | |||||
return getHandler()->setBounds (*this, drawable, newBounds); | |||||
} | |||||
void DrawableTypeInstance::getAllControlPoints (Array <RelativePoint>& points) | |||||
{ | |||||
return getHandler()->getAllControlPoints (*this, points); | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
namespace Tags | namespace Tags | ||||
{ | { | ||||
@@ -158,12 +84,6 @@ void DrawableDocument::checkRootObject() | |||||
if (markersY == 0) | if (markersY == 0) | ||||
markersY = new MarkerList (*this, false); | markersY = new MarkerList (*this, false); | ||||
/* if ((int) getCanvasWidth().getValue() <= 0) | |||||
getCanvasWidth() = 500; | |||||
if ((int) getCanvasHeight().getValue() <= 0) | |||||
getCanvasHeight() = 500; | |||||
*/ | |||||
DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode()); | DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode()); | ||||
recursivelyUpdateIDs (rootObject); | recursivelyUpdateIDs (rootObject); | ||||
} | } | ||||
@@ -325,35 +245,30 @@ const int menuItemOffset = 0x63451fa4; | |||||
void DrawableDocument::addNewItemMenuItems (PopupMenu& menu) const | void DrawableDocument::addNewItemMenuItems (PopupMenu& menu) const | ||||
{ | { | ||||
DrawableTypeManager* const typeMan = DrawableTypeManager::getInstance(); | |||||
const StringArray newItems (DrawableTypeManager::getInstance()->getNewItemList()); | |||||
for (int i = 0; i < typeMan->getNumHandlers(); ++i) | |||||
if (typeMan->getHandler(i)->canBeCreated) | |||||
menu.addItem (i + menuItemOffset, "New " + typeMan->getHandler(i)->getDisplayName()); | |||||
for (int i = 0; i < newItems.size(); ++i) | |||||
menu.addItem (i + menuItemOffset, newItems[i]); | |||||
} | } | ||||
const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode) | const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode) | ||||
{ | { | ||||
DrawableTypeManager* const typeMan = DrawableTypeManager::getInstance(); | |||||
const StringArray newItems (DrawableTypeManager::getInstance()->getNewItemList()); | |||||
if (menuResultCode >= menuItemOffset && menuResultCode < menuItemOffset + typeMan->getNumHandlers()) | |||||
int index = menuResultCode - menuItemOffset; | |||||
if (index >= 0 && index < newItems.size()) | |||||
{ | { | ||||
DrawableTypeHandler* handler = typeMan->getHandler (menuResultCode - menuItemOffset); | |||||
jassert (handler != 0); | |||||
ValueTree state (DrawableTypeManager::getInstance() | |||||
->createNewItem (index, *this, | |||||
Point<float> (Random::getSystemRandom().nextFloat() * 100.0f + 100.0f, | |||||
Random::getSystemRandom().nextFloat() * 100.0f + 100.0f))); | |||||
if (handler != 0) | |||||
{ | |||||
ValueTree state (handler->createNewInstance (*this, | |||||
Point<float> (Random::getSystemRandom().nextFloat() * 100.0f + 100.0f, | |||||
Random::getSystemRandom().nextFloat() * 100.0f + 100.0f))); | |||||
Drawable::ValueTreeWrapperBase wrapper (state); | |||||
recursivelyUpdateIDs (wrapper); | |||||
Drawable::ValueTreeWrapperBase wrapper (state); | |||||
recursivelyUpdateIDs (wrapper); | |||||
getRootDrawableNode().addDrawable (state, -1, getUndoManager()); | |||||
getRootDrawableNode().addDrawable (state, -1, getUndoManager()); | |||||
return state; | |||||
} | |||||
return state; | |||||
} | } | ||||
return ValueTree::invalid; | return ValueTree::invalid; | ||||
@@ -391,23 +306,11 @@ const RelativeCoordinate DrawableDocument::findNamedCoordinate (const String& ob | |||||
{ | { | ||||
if (objectName == "parent") | if (objectName == "parent") | ||||
{ | { | ||||
// if (edge == "right") return RelativeCoordinate ((double) getCanvasWidth().getValue(), true); | |||||
// if (edge == "bottom") return RelativeCoordinate ((double) getCanvasHeight().getValue(), false); | |||||
jassert (edge != "right" && edge != "bottom"); // drawables don't have a canvas size.. | |||||
} | } | ||||
if (objectName.isNotEmpty() && edge.isNotEmpty()) | if (objectName.isNotEmpty() && edge.isNotEmpty()) | ||||
{ | { | ||||
/* const ValueTree comp (getComponentWithMemberName (compName)); | |||||
if (comp.isValid()) | |||||
{ | |||||
const RelativeRectangle coords (getCoordsFor (comp)); | |||||
if (edge == RelativeCoordinate::leftName) return coords.left; | |||||
if (edge == "right") return coords.right; | |||||
if (edge == "top") return coords.top; | |||||
if (edge == "bottom") return coords.bottom; | |||||
}*/ | |||||
} | } | ||||
{ | { | ||||
@@ -473,8 +376,7 @@ const RelativeCoordinate DrawableDocument::MarkerList::findNamedCoordinate (cons | |||||
{ | { | ||||
if (objectName == "parent") | if (objectName == "parent") | ||||
{ | { | ||||
// if (edge == "right") return RelativeCoordinate ((double) document.getCanvasWidth().getValue(), true); | |||||
// if (edge == "bottom") return RelativeCoordinate ((double) document.getCanvasHeight().getValue(), false); | |||||
jassert (edge != "right" && edge != "bottom"); // drawables don't have a canvas size.. | |||||
} | } | ||||
const ValueTree marker (getMarkerNamed (objectName)); | const ValueTree marker (getMarkerNamed (objectName)); | ||||
@@ -0,0 +1,491 @@ | |||||
/* | |||||
============================================================================== | |||||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
Copyright 2004-10 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_DrawableTypeHandler.h" | |||||
//============================================================================== | |||||
class DrawablePathHandler : public DrawableTypeHandler | |||||
{ | |||||
public: | |||||
DrawablePathHandler() : DrawableTypeHandler ("Polygon", DrawablePath::valueTreeType) {} | |||||
~DrawablePathHandler() {} | |||||
static const ValueTree createNewPath (DrawableDocument& document, const Path& p) | |||||
{ | |||||
DrawablePath dp; | |||||
dp.setPath (p); | |||||
dp.setFill (Colours::lightblue.withHue (Random::getSystemRandom().nextFloat())); | |||||
return dp.createValueTree (0); | |||||
} | |||||
static const ValueTree createNewTriangle (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
Path p; | |||||
p.addTriangle (approxPosition.getX(), approxPosition.getY() - 50.0f, | |||||
approxPosition.getX() + 50.0f, approxPosition.getY() + 20.0f, | |||||
approxPosition.getX() - 50.0f, approxPosition.getY() + 20.0f); | |||||
return createNewPath (document, p); | |||||
} | |||||
static const ValueTree createNewRectangle (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
Path p; | |||||
p.addRectangle (approxPosition.getX() - 50.0f, approxPosition.getY() - 50.0f, | |||||
100.0f, 100.0f); | |||||
return createNewPath (document, p); | |||||
} | |||||
static const ValueTree createNewEllipse (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
Path p; | |||||
p.addEllipse (approxPosition.getX() - 50.0f, approxPosition.getY() - 50.0f, | |||||
100.0f, 100.0f); | |||||
return createNewPath (document, p); | |||||
} | |||||
class DrawablePathFillPropComp : public FillTypePropertyComponent | |||||
{ | |||||
public: | |||||
DrawablePathFillPropComp (DrawableTypeInstance& item_, const String& name, const ValueTree& fill) | |||||
: FillTypePropertyComponent (item_.getDocument().getUndoManager(), name, fill), | |||||
item (item_) | |||||
{} | |||||
const ColourGradient getDefaultGradient() | |||||
{ | |||||
const Rectangle<float> bounds (item.getBounds()); | |||||
return ColourGradient (Colours::blue, | |||||
bounds.getX() + bounds.getWidth() * 0.3f, | |||||
bounds.getY() + bounds.getHeight() * 0.3f, | |||||
Colours::red, | |||||
bounds.getX() + bounds.getWidth() * 0.7f, | |||||
bounds.getY() + bounds.getHeight() * 0.7f, | |||||
false); | |||||
} | |||||
private: | |||||
DrawableTypeInstance item; | |||||
}; | |||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||||
props.add (new DrawablePathFillPropComp (item, "Fill", wrapper.getMainFillState())); | |||||
props.add (new DrawablePathFillPropComp (item, "Stroke", wrapper.getStrokeFillState())); | |||||
} | |||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
{ | |||||
} | |||||
//============================================================================== | |||||
class GradientControlPoint : public ControlPoint | |||||
{ | |||||
public: | |||||
GradientControlPoint (const ValueTree& item_, | |||||
const bool isStart_, const bool isStroke_) | |||||
: item (item_), isStart (isStart_), isStroke (isStroke_) | |||||
{} | |||||
~GradientControlPoint() {} | |||||
const RelativePoint getPosition() | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item); | |||||
RelativePoint p; | |||||
const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState(), | |||||
isStart ? &p : 0, | |||||
isStart ? 0 : &p, 0)); | |||||
jassert (fill.isGradient()); | |||||
return p; | |||||
} | |||||
void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item); | |||||
RelativePoint p1, p2; | |||||
ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); | |||||
const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (fillState, &p1, &p2, 0)); | |||||
jassert (fill.isGradient()); | |||||
if (isStart) | |||||
p1 = newPoint; | |||||
else | |||||
p2 = newPoint; | |||||
Drawable::ValueTreeWrapperBase::writeFillType (fillState, fill, &p1, &p2, undoManager); | |||||
} | |||||
bool hasLine() { return false; } | |||||
RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
private: | |||||
ValueTree item; | |||||
bool isStart, isStroke; | |||||
}; | |||||
//============================================================================== | |||||
class PathControlPoint : public ControlPoint | |||||
{ | |||||
public: | |||||
PathControlPoint (const DrawablePath::ValueTreeWrapper::Element& element_, const int cpNum_) | |||||
: element (element_), cpNum (cpNum_) | |||||
{} | |||||
~PathControlPoint() {} | |||||
const RelativePoint getPosition() | |||||
{ | |||||
return element.getControlPoint (cpNum); | |||||
} | |||||
void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) | |||||
{ | |||||
element.setControlPoint (cpNum, newPoint, undoManager); | |||||
} | |||||
bool hasLine() { return false; } | |||||
RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
private: | |||||
DrawablePath::ValueTreeWrapper::Element element; | |||||
int cpNum; | |||||
}; | |||||
void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||||
const ValueTree pathTree (wrapper.getPathState()); | |||||
const int numElements = pathTree.getNumChildren(); | |||||
for (int i = 0; i < numElements; ++i) | |||||
{ | |||||
const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); | |||||
const int numCps = e.getNumControlPoints(); | |||||
for (int j = 0; j < numCps; ++j) | |||||
points.add (new PathControlPoint (e, j)); | |||||
} | |||||
const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getMainFillState(), 0, 0, 0)); | |||||
if (fill.isGradient()) | |||||
{ | |||||
points.add (new GradientControlPoint (item.getState(), true, false)); | |||||
points.add (new GradientControlPoint (item.getState(), false, false)); | |||||
} | |||||
const FillType stroke (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getStrokeFillState(), 0, 0, 0)); | |||||
if (stroke.isGradient()) | |||||
{ | |||||
points.add (new GradientControlPoint (item.getState(), true, true)); | |||||
points.add (new GradientControlPoint (item.getState(), false, true)); | |||||
} | |||||
} | |||||
}; | |||||
//============================================================================== | |||||
class DrawableImageHandler : public DrawableTypeHandler | |||||
{ | |||||
public: | |||||
DrawableImageHandler() : DrawableTypeHandler ("Image", DrawableImage::valueTreeType) {} | |||||
~DrawableImageHandler() {} | |||||
static const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
Image tempImage (Image::ARGB, 100, 100, true); | |||||
{ | |||||
Graphics g (tempImage); | |||||
g.fillAll (Colours::grey.withAlpha (0.3f)); | |||||
g.setColour (Colours::red); | |||||
g.setFont (40.0f); | |||||
g.drawText ("?", 0, 0, 100, 100, Justification::centred, false); | |||||
} | |||||
DrawableImage di; | |||||
di.setTransform (RelativePoint (approxPosition), | |||||
RelativePoint (approxPosition + Point<float> (100.0f, 0.0f)), | |||||
RelativePoint (approxPosition + Point<float> (0.0f, 100.0f))); | |||||
return di.createValueTree (&document); | |||||
} | |||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
{ | |||||
} | |||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
{ | |||||
} | |||||
//============================================================================== | |||||
class ImageControlPoint : public ControlPoint | |||||
{ | |||||
public: | |||||
ImageControlPoint (const DrawableTypeInstance& item_, const int cpNum_) | |||||
: item (item_), cpNum (cpNum_) | |||||
{} | |||||
~ImageControlPoint() {} | |||||
const RelativePoint getPosition() | |||||
{ | |||||
DrawableImage::ValueTreeWrapper wrapper (item.getState()); | |||||
switch (cpNum) | |||||
{ | |||||
case 0: return wrapper.getTargetPositionForTopLeft(); | |||||
case 1: return wrapper.getTargetPositionForTopRight(); | |||||
case 2: return wrapper.getTargetPositionForBottomLeft(); | |||||
default: jassertfalse; break; | |||||
} | |||||
return RelativePoint(); | |||||
} | |||||
void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) | |||||
{ | |||||
DrawableImage::ValueTreeWrapper wrapper (item.getState()); | |||||
switch (cpNum) | |||||
{ | |||||
case 0: wrapper.setTargetPositionForTopLeft (newPoint, undoManager); break; | |||||
case 1: wrapper.setTargetPositionForTopRight (newPoint, undoManager); break; | |||||
case 2: wrapper.setTargetPositionForBottomLeft (newPoint, undoManager); break; | |||||
default: jassertfalse; break; | |||||
} | |||||
} | |||||
bool hasLine() { return false; } | |||||
RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
private: | |||||
DrawableTypeInstance item; | |||||
int cpNum; | |||||
}; | |||||
void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | |||||
{ | |||||
for (int i = 0; i < 3; ++i) | |||||
points.add (new ImageControlPoint (item, i)); | |||||
} | |||||
}; | |||||
//============================================================================== | |||||
class DrawableCompositeHandler : public DrawableTypeHandler | |||||
{ | |||||
public: | |||||
DrawableCompositeHandler() : DrawableTypeHandler ("Group", DrawableComposite::valueTreeType) {} | |||||
~DrawableCompositeHandler() {} | |||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
{ | |||||
} | |||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
{ | |||||
} | |||||
const RelativeCoordinate findNamedCoordinate (const DrawableTypeInstance& item, const String& objectName, const String& edge) const | |||||
{ | |||||
DrawableComposite::ValueTreeWrapper wrapper (const_cast <DrawableTypeInstance&> (item).getState()); | |||||
ValueTree markerState (wrapper.getMarkerState (true, objectName)); | |||||
if (markerState.isValid()) | |||||
return wrapper.getMarker (true, markerState).position; | |||||
markerState = wrapper.getMarkerState (false, objectName); | |||||
if (markerState.isValid()) | |||||
return wrapper.getMarker (false, markerState).position; | |||||
return RelativeCoordinate(); | |||||
} | |||||
//============================================================================== | |||||
class CompositeControlPoint : public ControlPoint | |||||
{ | |||||
public: | |||||
CompositeControlPoint (const ValueTree& item_, const int cpNum_) | |||||
: item (item_), cpNum (cpNum_) | |||||
{} | |||||
~CompositeControlPoint() {} | |||||
const RelativePoint getPosition() | |||||
{ | |||||
DrawableComposite::ValueTreeWrapper wrapper (item); | |||||
switch (cpNum) | |||||
{ | |||||
case 0: return wrapper.getTargetPositionForOrigin(); | |||||
case 1: return wrapper.getTargetPositionForX1Y0(); | |||||
case 2: return wrapper.getTargetPositionForX0Y1(); | |||||
default: jassertfalse; break; | |||||
} | |||||
return RelativePoint(); | |||||
} | |||||
void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) | |||||
{ | |||||
DrawableComposite::ValueTreeWrapper wrapper (item); | |||||
switch (cpNum) | |||||
{ | |||||
case 0: wrapper.setTargetPositionForOrigin (newPoint, undoManager); break; | |||||
case 1: wrapper.setTargetPositionForX1Y0 (newPoint, undoManager); break; | |||||
case 2: wrapper.setTargetPositionForX0Y1 (newPoint, undoManager); break; | |||||
default: jassertfalse; break; | |||||
} | |||||
} | |||||
bool hasLine() { return false; } | |||||
RelativePoint getEndOfLine() { return RelativePoint(); } | |||||
private: | |||||
ValueTree item; | |||||
int cpNum; | |||||
}; | |||||
void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) | |||||
{ | |||||
for (int i = 0; i < 3; ++i) | |||||
points.add (new CompositeControlPoint (item.getState(), i)); | |||||
} | |||||
}; | |||||
//============================================================================== | |||||
DrawableTypeManager::DrawableTypeManager() | |||||
{ | |||||
handlers.add (new DrawablePathHandler()); | |||||
handlers.add (new DrawableImageHandler()); | |||||
handlers.add (new DrawableCompositeHandler()); | |||||
} | |||||
DrawableTypeManager::~DrawableTypeManager() | |||||
{ | |||||
} | |||||
DrawableTypeHandler* DrawableTypeManager::getHandlerFor (const Identifier& type) | |||||
{ | |||||
for (int i = handlers.size(); --i >= 0;) | |||||
if (handlers.getUnchecked(i)->getValueTreeType() == type) | |||||
return handlers.getUnchecked(i); | |||||
jassertfalse; | |||||
return 0; | |||||
} | |||||
const StringArray DrawableTypeManager::getNewItemList() | |||||
{ | |||||
const char* types[] = { "New Triangle", "New Rectangle", "New Ellipse", "New Image", 0 }; | |||||
return StringArray (types); | |||||
} | |||||
const ValueTree DrawableTypeManager::createNewItem (const int index, DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
switch (index) | |||||
{ | |||||
case 0: return DrawablePathHandler::createNewTriangle (document, approxPosition); | |||||
case 1: return DrawablePathHandler::createNewRectangle (document, approxPosition); | |||||
case 2: return DrawablePathHandler::createNewEllipse (document, approxPosition); | |||||
case 3: return DrawableImageHandler::createNewInstance (document, approxPosition); | |||||
default: jassertfalse; break; | |||||
} | |||||
return ValueTree::invalid; | |||||
} | |||||
juce_ImplementSingleton_SingleThreaded (DrawableTypeManager); | |||||
//============================================================================== | |||||
DrawableTypeInstance::DrawableTypeInstance (DrawableDocument& document_, const ValueTree& state_) | |||||
: document (document_), state (state_) | |||||
{ | |||||
} | |||||
Value DrawableTypeInstance::getValue (const Identifier& name) const | |||||
{ | |||||
return state.getPropertyAsValue (name, document.getUndoManager()); | |||||
} | |||||
void DrawableTypeInstance::createProperties (Array <PropertyComponent*>& props) | |||||
{ | |||||
props.add (new TextPropertyComponent (getValue (Drawable::ValueTreeWrapperBase::idProperty), "Object ID", 128, false)); | |||||
getHandler()->createPropertyEditors (*this, props); | |||||
} | |||||
DrawableTypeHandler* DrawableTypeInstance::getHandler() const | |||||
{ | |||||
DrawableTypeHandler* h = DrawableTypeManager::getInstance()->getHandlerFor (state.getType()); | |||||
jassert (h != 0); | |||||
return h; | |||||
} | |||||
const RelativeCoordinate DrawableTypeInstance::findNamedCoordinate (const String& objectName, const String& edge) const | |||||
{ | |||||
return getHandler()->findNamedCoordinate (*this, objectName, edge); | |||||
} | |||||
const Rectangle<float> DrawableTypeInstance::getBounds() | |||||
{ | |||||
OwnedArray <ControlPoint> points; | |||||
getAllControlPoints (points); | |||||
if (points.size() < 2) | |||||
return Rectangle<float>(); | |||||
DrawableTypeInstance parent (document, state.getParent()); | |||||
const Point<float> p1 (points.getUnchecked(0)->getPosition().resolve (&parent)); | |||||
Rectangle<float> r (p1, points.getUnchecked(1)->getPosition().resolve (&parent)); | |||||
for (int i = 2; i < points.size(); ++i) | |||||
r = r.getUnion (Rectangle<float> (p1, points.getUnchecked(i)->getPosition().resolve (&parent))); | |||||
return r; | |||||
} | |||||
void DrawableTypeInstance::setBounds (Drawable* drawable, const Rectangle<float>& newBounds) | |||||
{ | |||||
return getHandler()->setBounds (*this, drawable, newBounds); | |||||
} | |||||
void DrawableTypeInstance::getAllControlPoints (OwnedArray <ControlPoint>& points) | |||||
{ | |||||
return getHandler()->getAllControlPoints (*this, points); | |||||
} |
@@ -30,9 +30,22 @@ | |||||
#include "../../utility/jucer_FillTypePropertyComponent.h" | #include "../../utility/jucer_FillTypePropertyComponent.h" | ||||
class DrawableTypeHandler; | class DrawableTypeHandler; | ||||
//============================================================================== | |||||
class ControlPoint | |||||
{ | |||||
public: | |||||
ControlPoint() {} | |||||
virtual ~ControlPoint() {} | |||||
virtual const RelativePoint getPosition() = 0; | |||||
virtual void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) = 0; | |||||
virtual bool hasLine() = 0; | |||||
virtual RelativePoint getEndOfLine() = 0; | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
class DrawableTypeInstance | |||||
class DrawableTypeInstance : public RelativeCoordinate::NamedCoordinateFinder | |||||
{ | { | ||||
public: | public: | ||||
DrawableTypeInstance (DrawableDocument& document_, const ValueTree& state_); | DrawableTypeInstance (DrawableDocument& document_, const ValueTree& state_); | ||||
@@ -43,8 +56,11 @@ public: | |||||
Value getValue (const Identifier& name) const; | Value getValue (const Identifier& name) const; | ||||
void createProperties (Array <PropertyComponent*>& props); | void createProperties (Array <PropertyComponent*>& props); | ||||
const Rectangle<float> getBounds(); | |||||
void setBounds (Drawable* drawable, const Rectangle<float>& newBounds); | void setBounds (Drawable* drawable, const Rectangle<float>& newBounds); | ||||
void getAllControlPoints (Array <RelativePoint>& points); | |||||
void getAllControlPoints (OwnedArray <ControlPoint>& points); | |||||
const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; | |||||
//============================================================================== | //============================================================================== | ||||
DrawableTypeHandler* getHandler() const; | DrawableTypeHandler* getHandler() const; | ||||
@@ -61,31 +77,26 @@ private: | |||||
class DrawableTypeHandler | class DrawableTypeHandler | ||||
{ | { | ||||
public: | public: | ||||
DrawableTypeHandler (const String& displayName_, const Identifier& valueTreeType_, bool canBeCreated_) | |||||
: displayName (displayName_), valueTreeType (valueTreeType_), canBeCreated (canBeCreated_) | |||||
DrawableTypeHandler (const String& displayName_, const Identifier& valueTreeType_) | |||||
: displayName (displayName_), valueTreeType (valueTreeType_) | |||||
{ | { | ||||
} | } | ||||
virtual ~DrawableTypeHandler() {} | virtual ~DrawableTypeHandler() {} | ||||
virtual const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) = 0; | |||||
virtual void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) = 0; | virtual void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) = 0; | ||||
virtual void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) = 0; | virtual void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) = 0; | ||||
virtual void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds) = 0; | |||||
virtual void getAllControlPoints (DrawableTypeInstance& item, Array <RelativePoint>& points) = 0; | |||||
virtual void getAllControlPoints (DrawableTypeInstance& item, OwnedArray <ControlPoint>& points) = 0; | |||||
virtual const RelativeCoordinate findNamedCoordinate (const DrawableTypeInstance& item, const String& objectName, const String& edge) const { return RelativeCoordinate(); } | |||||
const String& getDisplayName() const { return displayName; } | const String& getDisplayName() const { return displayName; } | ||||
const Identifier& getValueTreeType() const { return valueTreeType; } | const Identifier& getValueTreeType() const { return valueTreeType; } | ||||
const bool canBeCreated; | |||||
protected: | |||||
static bool rescalePoints (RelativePoint* const points, const int numPoints, | |||||
const Rectangle<float>& oldBounds, Rectangle<float> newBounds, | |||||
RelativeCoordinate::NamedCoordinateFinder* nameFinder) | |||||
void setBounds (DrawableTypeInstance& item, Drawable* drawable, Rectangle<float> newBounds) | |||||
{ | { | ||||
const Rectangle<float> oldBounds (drawable->getBounds()); | |||||
if (oldBounds.isEmpty()) | if (oldBounds.isEmpty()) | ||||
return false; | |||||
return; | |||||
newBounds.setSize (jmax (1.0f, newBounds.getWidth()), | newBounds.setSize (jmax (1.0f, newBounds.getWidth()), | ||||
jmax (1.0f, newBounds.getHeight())); | jmax (1.0f, newBounds.getHeight())); | ||||
@@ -101,20 +112,28 @@ protected: | |||||
if (xScale == 1.0 && yScale == 1.0 | if (xScale == 1.0 && yScale == 1.0 | ||||
&& std::abs (newBounds.getX() - oldBounds.getX()) < tolerance | && std::abs (newBounds.getX() - oldBounds.getX()) < tolerance | ||||
&& std::abs (newBounds.getY() - oldBounds.getY()) < tolerance) | && std::abs (newBounds.getY() - oldBounds.getY()) < tolerance) | ||||
return false; | |||||
return; | |||||
const double xOffset = newBounds.getX() - xScale * oldBounds.getX(); | const double xOffset = newBounds.getX() - xScale * oldBounds.getX(); | ||||
const double yOffset = newBounds.getY() - yScale * oldBounds.getY(); | const double yOffset = newBounds.getY() - yScale * oldBounds.getY(); | ||||
for (int i = 0; i < numPoints; ++i) | |||||
OwnedArray<ControlPoint> points; | |||||
getAllControlPoints (item, points); | |||||
RelativeCoordinate::NamedCoordinateFinder* const nameFinder = drawable->getParent(); | |||||
UndoManager* undoManager = item.getDocument().getUndoManager(); | |||||
for (int i = 0; i < points.size(); ++i) | |||||
{ | { | ||||
const Point<float> p (points[i].resolve (nameFinder)); | |||||
ControlPoint* cp = points.getUnchecked(i); | |||||
RelativePoint point (cp->getPosition()); | |||||
const Point<float> p (point.resolve (nameFinder)); | |||||
points[i].moveToAbsolute (Point<float> ((float) (xOffset + xScale * p.getX()), | |||||
(float) (yOffset + yScale * p.getY())), nameFinder); | |||||
} | |||||
point.moveToAbsolute (Point<float> ((float) (xOffset + xScale * p.getX()), | |||||
(float) (yOffset + yScale * p.getY())), nameFinder); | |||||
return true; | |||||
cp->setPosition (point, undoManager); | |||||
} | |||||
} | } | ||||
private: | private: | ||||
@@ -124,200 +143,28 @@ private: | |||||
DrawableTypeHandler& operator= (const DrawableTypeHandler&); | DrawableTypeHandler& operator= (const DrawableTypeHandler&); | ||||
}; | }; | ||||
//============================================================================== | |||||
class DrawablePathHandler : public DrawableTypeHandler | |||||
{ | |||||
public: | |||||
DrawablePathHandler() : DrawableTypeHandler ("Polygon", DrawablePath::valueTreeType, true) {} | |||||
~DrawablePathHandler() {} | |||||
const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
Path p; | |||||
p.addTriangle (approxPosition.getX(), approxPosition.getY() - 50.0f, | |||||
approxPosition.getX() + 50.0f, approxPosition.getY() + 20.0f, | |||||
approxPosition.getX() - 50.0f, approxPosition.getY() + 20.0f); | |||||
DrawablePath dp; | |||||
dp.setPath (p); | |||||
dp.setFill (Colours::lightblue.withHue (Random::getSystemRandom().nextFloat())); | |||||
return dp.createValueTree (0); | |||||
} | |||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||||
props.add (new FillTypePropertyComponent (item.getDocument().getUndoManager(), | |||||
"Fill", wrapper.getMainFillState())); | |||||
props.add (new FillTypePropertyComponent (item.getDocument().getUndoManager(), | |||||
"Stroke", wrapper.getStrokeFillState())); | |||||
} | |||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
{ | |||||
} | |||||
void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||||
RelativePointPath path; | |||||
wrapper.getPath (path); | |||||
Array <RelativePoint> points; | |||||
int i; | |||||
for (i = 0; i < path.elements.size(); ++i) | |||||
{ | |||||
int numPoints; | |||||
RelativePoint* elementPoints = path.elements.getUnchecked(i)->getControlPoints (numPoints); | |||||
for (int j = 0; j < numPoints; ++j) | |||||
points.add (elementPoints[j]); | |||||
} | |||||
if (rescalePoints (points.getRawDataPointer(), points.size(), | |||||
drawable->getBounds(), newBounds, drawable->getParent())) | |||||
{ | |||||
int n = 0; | |||||
for (i = 0; i < path.elements.size(); ++i) | |||||
{ | |||||
int numPoints; | |||||
RelativePoint* elementPoints = path.elements.getUnchecked(i)->getControlPoints (numPoints); | |||||
for (int j = 0; j < numPoints; ++j) | |||||
elementPoints[j] = points [n++]; | |||||
} | |||||
wrapper.setPath (path.toString(), item.getDocument().getUndoManager()); | |||||
} | |||||
} | |||||
void getAllControlPoints (DrawableTypeInstance& item, Array <RelativePoint>& points) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (item.getState()); | |||||
RelativePointPath path; | |||||
wrapper.getPath (path); | |||||
for (int i = 0; i < path.elements.size(); ++i) | |||||
{ | |||||
int numPoints; | |||||
RelativePoint* elementPoints = path.elements.getUnchecked(i)->getControlPoints (numPoints); | |||||
for (int j = 0; j < numPoints; ++j) | |||||
points.add (elementPoints[j]); | |||||
} | |||||
} | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
class DrawableImageHandler : public DrawableTypeHandler | |||||
class DrawableTypeManager : public DeletedAtShutdown | |||||
{ | { | ||||
public: | public: | ||||
DrawableImageHandler() : DrawableTypeHandler ("Image", DrawableImage::valueTreeType, true) {} | |||||
~DrawableImageHandler() {} | |||||
const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
Image tempImage (Image::ARGB, 100, 100, true); | |||||
{ | |||||
Graphics g (tempImage); | |||||
g.fillAll (Colours::grey.withAlpha (0.3f)); | |||||
g.setColour (Colours::red); | |||||
g.setFont (40.0f); | |||||
g.drawText ("?", 0, 0, 100, 100, Justification::centred, false); | |||||
} | |||||
DrawableTypeManager(); | |||||
~DrawableTypeManager(); | |||||
DrawableImage di; | |||||
di.setTransform (RelativePoint (approxPosition), | |||||
RelativePoint (approxPosition + Point<float> (100.0f, 0.0f)), | |||||
RelativePoint (approxPosition + Point<float> (0.0f, 100.0f))); | |||||
return di.createValueTree (&document); | |||||
} | |||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
{ | |||||
} | |||||
juce_DeclareSingleton_SingleThreaded_Minimal (DrawableTypeManager); | |||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
{ | |||||
} | |||||
void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds) | |||||
{ | |||||
DrawableImage::ValueTreeWrapper wrapper (item.getState()); | |||||
//============================================================================== | |||||
int getNumHandlers() const { return handlers.size(); } | |||||
DrawableTypeHandler* getHandler (const int index) const { return handlers [index]; } | |||||
RelativePoint points[3] = { wrapper.getTargetPositionForTopLeft(), | |||||
wrapper.getTargetPositionForTopRight(), | |||||
wrapper.getTargetPositionForBottomLeft() }; | |||||
DrawableTypeHandler* getHandlerFor (const Identifier& type); | |||||
if (rescalePoints (points, 3, drawable->getBounds(), newBounds, drawable->getParent())) | |||||
{ | |||||
UndoManager* undoManager = item.getDocument().getUndoManager(); | |||||
wrapper.setTargetPositionForTopLeft (points[0], undoManager); | |||||
wrapper.setTargetPositionForTopRight (points[1], undoManager); | |||||
wrapper.setTargetPositionForBottomLeft (points[2], undoManager); | |||||
} | |||||
} | |||||
const StringArray getNewItemList(); | |||||
const ValueTree createNewItem (const int index, DrawableDocument& document, const Point<float>& approxPosition); | |||||
void getAllControlPoints (DrawableTypeInstance& item, Array <RelativePoint>& points) | |||||
{ | |||||
DrawableImage::ValueTreeWrapper wrapper (item.getState()); | |||||
points.add (wrapper.getTargetPositionForTopLeft()); | |||||
points.add (wrapper.getTargetPositionForTopRight()); | |||||
points.add (wrapper.getTargetPositionForBottomLeft()); | |||||
} | |||||
private: | |||||
OwnedArray <DrawableTypeHandler> handlers; | |||||
}; | }; | ||||
//============================================================================== | |||||
class DrawableCompositeHandler : public DrawableTypeHandler | |||||
{ | |||||
public: | |||||
DrawableCompositeHandler() : DrawableTypeHandler ("Group", DrawableComposite::valueTreeType, false) {} | |||||
~DrawableCompositeHandler() {} | |||||
const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) | |||||
{ | |||||
return ValueTree::invalid; | |||||
} | |||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) | |||||
{ | |||||
} | |||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) | |||||
{ | |||||
} | |||||
void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds) | |||||
{ | |||||
DrawableComposite::ValueTreeWrapper wrapper (item.getState()); | |||||
RelativePoint points[3] = { wrapper.getTargetPositionForOrigin(), | |||||
wrapper.getTargetPositionForX1Y0(), | |||||
wrapper.getTargetPositionForX0Y1() }; | |||||
if (rescalePoints (points, 3, drawable->getBounds(), newBounds, drawable->getParent())) | |||||
{ | |||||
UndoManager* undoManager = item.getDocument().getUndoManager(); | |||||
wrapper.setTargetPositionForOrigin (points[0], undoManager); | |||||
wrapper.setTargetPositionForX1Y0 (points[1], undoManager); | |||||
wrapper.setTargetPositionForX0Y1 (points[2], undoManager); | |||||
} | |||||
} | |||||
void getAllControlPoints (DrawableTypeInstance& item, Array <RelativePoint>& points) | |||||
{ | |||||
DrawableComposite::ValueTreeWrapper wrapper (item.getState()); | |||||
points.add (wrapper.getTargetPositionForOrigin()); | |||||
points.add (wrapper.getTargetPositionForX1Y0()); | |||||
points.add (wrapper.getTargetPositionForX0Y1()); | |||||
} | |||||
}; | |||||
#endif // __JUCER_DRAWABLETYPEHANDLER_H_7FB02E2F__ | #endif // __JUCER_DRAWABLETYPEHANDLER_H_7FB02E2F__ |
@@ -146,7 +146,7 @@ public: | |||||
return getDocument().getCoordsFor (state); | return getDocument().getCoordsFor (state); | ||||
} | } | ||||
void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray<OverlayItemComponent>& existingComps) | |||||
void updateControlPointComponents (Component* parent, OwnedArray<OverlayItemComponent>& existingComps) | |||||
{ | { | ||||
} | } | ||||
@@ -184,7 +184,6 @@ public: | |||||
~DragOperation() | ~DragOperation() | ||||
{ | { | ||||
getUndoManager().beginNewTransaction(); | |||||
} | } | ||||
protected: | protected: | ||||
@@ -35,6 +35,7 @@ class DrawableEditorCanvas : public EditorCanvasBase, | |||||
public Timer | public Timer | ||||
{ | { | ||||
public: | public: | ||||
//============================================================================== | |||||
DrawableEditorCanvas (DrawableEditor& editor_) | DrawableEditorCanvas (DrawableEditor& editor_) | ||||
: editor (editor_) | : editor (editor_) | ||||
{ | { | ||||
@@ -48,6 +49,11 @@ public: | |||||
shutdown(); | shutdown(); | ||||
} | } | ||||
//============================================================================== | |||||
UndoManager& getUndoManager() throw() { return *getDocument().getUndoManager(); } | |||||
DrawableEditor& getEditor() throw() { return editor; } | |||||
DrawableDocument& getDocument() throw() { return editor.getDocument(); } | |||||
Component* createComponentHolder() | Component* createComponentHolder() | ||||
{ | { | ||||
return new DrawableComponent (this); | return new DrawableComponent (this); | ||||
@@ -67,7 +73,7 @@ public: | |||||
else | else | ||||
{ | { | ||||
const Rectangle<float> damage (drawable->refreshFromValueTree (doc.getRootDrawableNode().getState(), &doc)); | const Rectangle<float> damage (drawable->refreshFromValueTree (doc.getRootDrawableNode().getState(), &doc)); | ||||
getComponentHolder()->repaint (damage.getSmallestIntegerContainer()); | |||||
getComponentHolder()->repaint (objectSpaceToScreenSpace (damage.getSmallestIntegerContainer())); | |||||
} | } | ||||
startTimer (500); | startTimer (500); | ||||
@@ -81,14 +87,10 @@ public: | |||||
void setCanvasBounds (const Rectangle<int>& newBounds) {} | void setCanvasBounds (const Rectangle<int>& newBounds) {} | ||||
bool canResizeCanvas() const { return false; } | bool canResizeCanvas() const { return false; } | ||||
MarkerListBase& getMarkerList (bool isX) | |||||
{ | |||||
return getDocument().getMarkerList (isX); | |||||
} | |||||
double limitMarkerPosition (double pos) | |||||
//============================================================================== | |||||
const ValueTree getObjectState (const String& objectId) | |||||
{ | { | ||||
return pos; | |||||
return getDocument().findDrawableState (objectId, false); | |||||
} | } | ||||
const SelectedItems::ItemType findObjectIdAt (const Point<int>& position) | const SelectedItems::ItemType findObjectIdAt (const Point<int>& position) | ||||
@@ -130,30 +132,36 @@ public: | |||||
void objectDoubleClicked (const MouseEvent& e, const ValueTree& state) | void objectDoubleClicked (const MouseEvent& e, const ValueTree& state) | ||||
{ | { | ||||
if (state.hasType (DrawablePath::valueTreeType) | |||||
|| state.hasType (DrawableImage::valueTreeType) | |||||
|| state.hasType (DrawableText::valueTreeType)) | |||||
{ | |||||
enableControlPointMode (state); | |||||
} | |||||
else if (state.hasType (DrawableComposite::valueTreeType)) | |||||
{ | |||||
// xxx | |||||
} | |||||
} | } | ||||
bool hasSizeGuides() const { return false; } | bool hasSizeGuides() const { return false; } | ||||
const ValueTree getObjectState (const String& objectId) | |||||
{ | |||||
return getDocument().findDrawableState (objectId, false); | |||||
} | |||||
void getObjectPositionDependencies (const ValueTree& state, Array<ValueTree>& deps) | void getObjectPositionDependencies (const ValueTree& state, Array<ValueTree>& deps) | ||||
{ | { | ||||
DrawableDocument& doc = getDocument(); | DrawableDocument& doc = getDocument(); | ||||
DrawableTypeInstance item (doc, state); | DrawableTypeInstance item (doc, state); | ||||
Array <RelativePoint> points; | |||||
OwnedArray <ControlPoint> points; | |||||
item.getAllControlPoints (points); | item.getAllControlPoints (points); | ||||
StringArray anchors; | StringArray anchors; | ||||
for (int i = 0; i < points.size(); ++i) | for (int i = 0; i < points.size(); ++i) | ||||
{ | { | ||||
anchors.addIfNotAlreadyThere (points.getReference(i).x.getAnchorName1()); | |||||
anchors.addIfNotAlreadyThere (points.getReference(i).x.getAnchorName2()); | |||||
anchors.addIfNotAlreadyThere (points.getReference(i).y.getAnchorName1()); | |||||
anchors.addIfNotAlreadyThere (points.getReference(i).y.getAnchorName2()); | |||||
const RelativePoint p (points.getUnchecked(i)->getPosition()); | |||||
anchors.addIfNotAlreadyThere (p.x.getAnchorName1()); | |||||
anchors.addIfNotAlreadyThere (p.x.getAnchorName2()); | |||||
anchors.addIfNotAlreadyThere (p.y.getAnchorName1()); | |||||
anchors.addIfNotAlreadyThere (p.y.getAnchorName2()); | |||||
} | } | ||||
for (int i = 0; i < anchors.size(); ++i) | for (int i = 0; i < anchors.size(); ++i) | ||||
@@ -207,12 +215,15 @@ public: | |||||
return RelativeRectangle(); | return RelativeRectangle(); | ||||
} | } | ||||
//============================================================================== | |||||
class ControlPointComponent : public OverlayItemComponent | class ControlPointComponent : public OverlayItemComponent | ||||
{ | { | ||||
public: | public: | ||||
ControlPointComponent (DrawableEditorCanvas* canvas) | |||||
: OverlayItemComponent (canvas) | |||||
ControlPointComponent (DrawableEditorCanvas* canvas, const ValueTree& drawableState_, int controlPointNum_) | |||||
: OverlayItemComponent (canvas), drawableState (drawableState_), | |||||
controlPointNum (controlPointNum_), isDragging (false), mouseDownResult (false), selected (false) | |||||
{ | { | ||||
selectionId = getControlPointId (drawableState, controlPointNum); | |||||
} | } | ||||
~ControlPointComponent() | ~ControlPointComponent() | ||||
@@ -221,29 +232,79 @@ public: | |||||
void paint (Graphics& g) | void paint (Graphics& g) | ||||
{ | { | ||||
g.fillAll (Colours::pink); | |||||
g.setColour (Colour (selected ? 0xaaaaaaaa : 0xaa333333)); | |||||
g.drawRect (0, 0, getWidth(), getHeight()); | |||||
g.setColour (Colour (selected ? 0xaa000000 : 0x99ffffff)); | |||||
g.fillRect (1, 1, getWidth() - 2, getHeight() - 2); | |||||
} | } | ||||
void mouseDown (const MouseEvent& e) | void mouseDown (const MouseEvent& e) | ||||
{ | { | ||||
isDragging = false; | |||||
if (e.mods.isPopupMenu()) | |||||
{ | |||||
canvas->showPopupMenu (true); | |||||
} | |||||
else | |||||
{ | |||||
mouseDownResult = canvas->getSelection().addToSelectionOnMouseDown (selectionId, e.mods); | |||||
} | |||||
} | } | ||||
void mouseDrag (const MouseEvent& e) | void mouseDrag (const MouseEvent& e) | ||||
{ | { | ||||
if (! (isDragging || e.mouseWasClicked() || e.mods.isPopupMenu())) | |||||
{ | |||||
canvas->getSelection().addToSelectionOnMouseUp (selectionId, e.mods, true, mouseDownResult); | |||||
isDragging = true; | |||||
canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), | |||||
ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||||
} | |||||
if (isDragging) | |||||
{ | |||||
canvas->continueDrag (e.getEventRelativeTo (getParentComponent())); | |||||
autoScrollForMouseEvent (e); | |||||
} | |||||
} | } | ||||
void mouseUp (const MouseEvent& e) | void mouseUp (const MouseEvent& e) | ||||
{ | |||||
if (isDragging) | |||||
{ | |||||
canvas->endDrag (e.getEventRelativeTo (getParentComponent())); | |||||
} | |||||
} | |||||
void mouseDoubleClick (const MouseEvent& e) | |||||
{ | { | ||||
} | } | ||||
void updatePosition (const RelativePoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) | |||||
void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) | |||||
{ | { | ||||
const Point<float> p (point.resolve (nameFinder)); | |||||
setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 5, 5)); | |||||
const Point<float> p (point.getPosition().resolve (nameFinder)); | |||||
setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 7, 7)); | |||||
const bool nowSelected = canvas->getSelection().isSelected (selectionId); | |||||
if (selected != nowSelected) | |||||
{ | |||||
selected = nowSelected; | |||||
repaint(); | |||||
} | |||||
} | } | ||||
private: | |||||
ValueTree drawableState; | |||||
int controlPointNum; | |||||
bool isDragging, mouseDownResult, selected; | |||||
String selectionId; | |||||
}; | }; | ||||
void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray<OverlayItemComponent>& comps) | |||||
void updateControlPointComponents (Component* parent, OwnedArray<OverlayItemComponent>& comps) | |||||
{ | { | ||||
if (drawable == 0) | if (drawable == 0) | ||||
{ | { | ||||
@@ -251,11 +312,11 @@ public: | |||||
return; | return; | ||||
} | } | ||||
DrawableTypeInstance item (getDocument(), state); | |||||
Array<RelativePoint> points; | |||||
DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | |||||
OwnedArray <ControlPoint> points; | |||||
item.getAllControlPoints (points); | item.getAllControlPoints (points); | ||||
Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (state).getID()); | |||||
Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); | |||||
DrawableComposite* parentDrawable = d->getParent(); | DrawableComposite* parentDrawable = d->getParent(); | ||||
comps.removeRange (points.size(), comps.size()); | comps.removeRange (points.size(), comps.size()); | ||||
@@ -269,15 +330,27 @@ public: | |||||
if (c == 0) | if (c == 0) | ||||
{ | { | ||||
c = new ControlPointComponent (this); | |||||
c = new ControlPointComponent (this, controlPointEditingTarget, i); | |||||
comps.set (i, c); | comps.set (i, c); | ||||
parent->addAndMakeVisible (c); | parent->addAndMakeVisible (c); | ||||
} | } | ||||
c->updatePosition (points.getReference(i), parentDrawable); | |||||
c->updatePosition (*points.getUnchecked(i), parentDrawable); | |||||
} | } | ||||
} | } | ||||
//============================================================================== | |||||
MarkerListBase& getMarkerList (bool isX) | |||||
{ | |||||
return getDocument().getMarkerList (isX); | |||||
} | |||||
double limitMarkerPosition (double pos) | |||||
{ | |||||
return pos; | |||||
} | |||||
//============================================================================== | |||||
SelectedItems& getSelection() | SelectedItems& getSelection() | ||||
{ | { | ||||
return editor.getSelection(); | return editor.getSelection(); | ||||
@@ -303,25 +376,84 @@ public: | |||||
} | } | ||||
} | } | ||||
bool isControlPointId (const String& itemId) | |||||
{ | |||||
return itemId.containsChar ('/'); | |||||
} | |||||
static const String getControlPointId (const ValueTree& drawableState, int index) | |||||
{ | |||||
return Drawable::ValueTreeWrapperBase (drawableState).getID() + "/" + String (index); | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
class DragOperation : public EditorDragOperation | |||||
class ObjectDragOperation : public EditorDragOperation | |||||
{ | { | ||||
public: | public: | ||||
DragOperation (DrawableEditorCanvas* canvas_, | |||||
const MouseEvent& e, const Point<int>& mousePos, | |||||
Component* snapGuideParentComp_, | |||||
const ResizableBorderComponent::Zone& zone_) | |||||
: EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_) | |||||
ObjectDragOperation (DrawableEditorCanvas* canvas_, | |||||
const MouseEvent& e, const Point<int>& mousePos, | |||||
Component* snapGuideParentComp_, | |||||
const ResizableBorderComponent::Zone& zone_) | |||||
: EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), drawableCanvas (canvas_) | |||||
{ | { | ||||
} | } | ||||
~DragOperation() | |||||
~ObjectDragOperation() {} | |||||
protected: | |||||
DrawableDocument& getDocument() throw() { return drawableCanvas->getDocument(); } | |||||
void getSnapPointsX (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); } | |||||
void getSnapPointsY (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); } | |||||
UndoManager& getUndoManager() { return *getDocument().getUndoManager(); } | |||||
void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps) | |||||
{ | { | ||||
getUndoManager().beginNewTransaction(); | |||||
drawableCanvas->getObjectPositionDependencies (state, deps); | |||||
} | |||||
const Rectangle<float> getObjectPosition (const ValueTree& state) | |||||
{ | |||||
return drawableCanvas->getObjectPositionFloat (state); | |||||
} | |||||
void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) | |||||
{ | |||||
drawableCanvas->setObjectPositionFloat (state, newBounds); | |||||
} | |||||
float getMarkerPosition (const ValueTree& marker, bool isX) | |||||
{ | |||||
return 0; | |||||
} | } | ||||
private: | |||||
DrawableEditorCanvas* drawableCanvas; | |||||
}; | |||||
//============================================================================== | |||||
class ControlPointDragOperation : public EditorDragOperation | |||||
{ | |||||
public: | |||||
ControlPointDragOperation (DrawableEditorCanvas* canvas_, | |||||
const DrawableTypeInstance& drawableItem_, | |||||
Drawable* drawable_, | |||||
const MouseEvent& e, const Point<int>& mousePos, | |||||
Component* snapGuideParentComp_, | |||||
const ResizableBorderComponent::Zone& zone_) | |||||
: EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), | |||||
drawableCanvas (canvas_), drawableItem (drawableItem_), drawable (drawable_) | |||||
{ | |||||
drawableItem.getAllControlPoints (points); | |||||
} | |||||
~ControlPointDragOperation() {} | |||||
OwnedArray <ControlPoint> points; | |||||
protected: | protected: | ||||
DrawableDocument& getDocument() throw() { return static_cast <DrawableEditorCanvas*> (canvas)->getDocument(); } | |||||
DrawableDocument& getDocument() throw() { return drawableCanvas->getDocument(); } | |||||
void getSnapPointsX (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); } | void getSnapPointsX (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); } | ||||
void getSnapPointsY (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); } | void getSnapPointsY (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); } | ||||
@@ -330,55 +462,91 @@ public: | |||||
void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps) | void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps) | ||||
{ | { | ||||
static_cast <DrawableEditorCanvas*> (canvas)->getObjectPositionDependencies (state, deps); | |||||
drawableCanvas->getObjectPositionDependencies (drawableItem.getState(), deps); | |||||
} | } | ||||
const Rectangle<float> getObjectPosition (const ValueTree& state) | const Rectangle<float> getObjectPosition (const ValueTree& state) | ||||
{ | { | ||||
return static_cast <DrawableEditorCanvas*> (canvas)->getObjectPositionFloat (state); | |||||
int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); | |||||
ControlPoint* cp = points[index]; | |||||
if (cp == 0) | |||||
return Rectangle<float>(); | |||||
Point<float> p (cp->getPosition().resolve (drawable->getParent())); | |||||
return Rectangle<float> (p, p); | |||||
} | } | ||||
void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) | void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds) | ||||
{ | { | ||||
static_cast <DrawableEditorCanvas*> (canvas)->setObjectPositionFloat (state, newBounds); | |||||
int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); | |||||
ControlPoint* cp = points[index]; | |||||
if (cp != 0) | |||||
{ | |||||
RelativePoint p (cp->getPosition()); | |||||
p.moveToAbsolute (newBounds.getPosition(), drawable->getParent()); | |||||
cp->setPosition (p, getDocument().getUndoManager()); | |||||
} | |||||
} | } | ||||
float getMarkerPosition (const ValueTree& marker, bool isX) | float getMarkerPosition (const ValueTree& marker, bool isX) | ||||
{ | { | ||||
return 0; | return 0; | ||||
} | } | ||||
private: | |||||
DrawableEditorCanvas* drawableCanvas; | |||||
DrawableTypeInstance drawableItem; | |||||
Drawable* drawable; | |||||
}; | }; | ||||
//============================================================================== | |||||
DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent, | DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent, | ||||
const ResizableBorderComponent::Zone& zone) | const ResizableBorderComponent::Zone& zone) | ||||
{ | { | ||||
DragOperation* d = new DragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); | |||||
Array<ValueTree> selected, unselected; | Array<ValueTree> selected, unselected; | ||||
EditorDragOperation* drag = 0; | |||||
DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); | |||||
for (int i = mainGroup.getNumDrawables(); --i >= 0;) | |||||
if (isControlPointMode()) | |||||
{ | { | ||||
const ValueTree v (mainGroup.getDrawableState (i)); | |||||
Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); | |||||
DrawableTypeInstance item (getDocument(), controlPointEditingTarget); | |||||
if (editor.getSelection().isSelected (v[Drawable::ValueTreeWrapperBase::idProperty])) | |||||
selected.add (v); | |||||
else | |||||
unselected.add (v); | |||||
ControlPointDragOperation* cpd = new ControlPointDragOperation (this, item, d, e, e.getPosition() - origin, snapGuideParentComponent, zone); | |||||
drag = cpd; | |||||
for (int i = 0; i < cpd->points.size(); ++i) | |||||
{ | |||||
const String pointId (getControlPointId (item.getState(), i)); | |||||
ValueTree v (Ids::controlPoint); | |||||
v.setProperty (Ids::id_, pointId, 0); | |||||
if (editor.getSelection().isSelected (pointId)) | |||||
selected.add (v); | |||||
else | |||||
unselected.add (v); | |||||
} | |||||
} | } | ||||
else | |||||
{ | |||||
drag = new ObjectDragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); | |||||
d->initialise (selected, unselected); | |||||
return d; | |||||
} | |||||
DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); | |||||
UndoManager& getUndoManager() | |||||
{ | |||||
return *getDocument().getUndoManager(); | |||||
} | |||||
for (int i = mainGroup.getNumDrawables(); --i >= 0;) | |||||
{ | |||||
const ValueTree v (mainGroup.getDrawableState (i)); | |||||
DrawableEditor& getEditor() throw() { return editor; } | |||||
DrawableDocument& getDocument() throw() { return editor.getDocument(); } | |||||
if (editor.getSelection().isSelected (v[Drawable::ValueTreeWrapperBase::idProperty])) | |||||
selected.add (v); | |||||
else | |||||
unselected.add (v); | |||||
} | |||||
} | |||||
drag->initialise (selected, unselected); | |||||
return drag; | |||||
} | |||||
void timerCallback() | void timerCallback() | ||||
{ | { | ||||
@@ -69,7 +69,7 @@ public: | |||||
else | else | ||||
{ | { | ||||
isDragging = true; | isDragging = true; | ||||
canvas->beginDrag (e.getEventRelativeTo (getParentComponent()), dragZone); | |||||
canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), dragZone); | |||||
canvas->showSizeGuides(); | canvas->showSizeGuides(); | ||||
} | } | ||||
} | } | ||||
@@ -121,7 +121,6 @@ public: | |||||
sizeGuides.getUnchecked(i)->updatePosition (bounds); | sizeGuides.getUnchecked(i)->updatePosition (bounds); | ||||
} | } | ||||
canvas->updateExtraComponentsForObject (objectState, getParentComponent(), extraEditorComps); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -202,7 +201,6 @@ private: | |||||
const int borderThickness; | const int borderThickness; | ||||
OwnedArray <SizeGuideComponent> sizeGuides; | OwnedArray <SizeGuideComponent> sizeGuides; | ||||
bool isDragging; | bool isDragging; | ||||
OwnedArray <OverlayItemComponent> extraEditorComps; | |||||
const Rectangle<int> getCentreArea() const | const Rectangle<int> getCentreArea() const | ||||
{ | { | ||||
@@ -404,6 +402,7 @@ public: | |||||
resizers.clear(); | resizers.clear(); | ||||
markersX.clear(); | markersX.clear(); | ||||
markersY.clear(); | markersY.clear(); | ||||
controlPoints.clear(); | |||||
deleteAllChildren(); | deleteAllChildren(); | ||||
} | } | ||||
@@ -420,7 +419,10 @@ public: | |||||
if (e.mods.isPopupMenu()) | if (e.mods.isPopupMenu()) | ||||
{ | { | ||||
if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse)) | if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse)) | ||||
{ | |||||
canvas->enableResizingMode(); | |||||
getSelection().selectOnly (underMouse); | getSelection().selectOnly (underMouse); | ||||
} | |||||
canvas->showPopupMenu (underMouse.isNotEmpty()); | canvas->showPopupMenu (underMouse.isNotEmpty()); | ||||
} | } | ||||
@@ -436,6 +438,7 @@ public: | |||||
{ | { | ||||
mouseDownCompUID = underMouse; | mouseDownCompUID = underMouse; | ||||
canvas->deselectNonDraggableObjects(); | canvas->deselectNonDraggableObjects(); | ||||
canvas->enableResizingMode(); | |||||
mouseDownResult = getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods); | mouseDownResult = getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods); | ||||
updateResizeFrames(); | updateResizeFrames(); | ||||
@@ -456,8 +459,9 @@ public: | |||||
if (! isDraggingClickedComp) | if (! isDraggingClickedComp) | ||||
{ | { | ||||
isDraggingClickedComp = true; | isDraggingClickedComp = true; | ||||
canvas->enableResizingMode(); | |||||
getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | ||||
canvas->beginDrag (e, ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||||
canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | |||||
} | } | ||||
canvas->continueDrag (e); | canvas->continueDrag (e); | ||||
@@ -526,15 +530,9 @@ public: | |||||
SelectedItems& getSelection() { return canvas->getSelection(); } | SelectedItems& getSelection() { return canvas->getSelection(); } | ||||
SelectedItems& getLassoSelection() { return getSelection(); } | SelectedItems& getLassoSelection() { return getSelection(); } | ||||
void resized() | |||||
{ | |||||
updateMarkers(); | |||||
updateResizeFrames(); | |||||
} | |||||
void changeListenerCallback (void*) | void changeListenerCallback (void*) | ||||
{ | { | ||||
updateResizeFrames(); | |||||
update(); | |||||
} | } | ||||
void modifierKeysChanged (const ModifierKeys&) | void modifierKeysChanged (const ModifierKeys&) | ||||
@@ -571,6 +569,7 @@ public: | |||||
void update() | void update() | ||||
{ | { | ||||
updateResizeFrames(); | updateResizeFrames(); | ||||
updateControlPoints(); | |||||
updateMarkers(); | updateMarkers(); | ||||
} | } | ||||
@@ -582,9 +581,16 @@ private: | |||||
SelectedItems::ItemType mouseDownCompUID; | SelectedItems::ItemType mouseDownCompUID; | ||||
OwnedArray <ResizeFrame> resizers; | OwnedArray <ResizeFrame> resizers; | ||||
OwnedArray <MarkerComponent> markersX, markersY; | OwnedArray <MarkerComponent> markersX, markersY; | ||||
OwnedArray <OverlayItemComponent> controlPoints; | |||||
void updateResizeFrames() | void updateResizeFrames() | ||||
{ | { | ||||
if (! canvas->isResizingMode()) | |||||
{ | |||||
resizers.clear(); | |||||
return; | |||||
} | |||||
SelectedItems& selection = getSelection(); | SelectedItems& selection = getSelection(); | ||||
StringArray requiredIds; | StringArray requiredIds; | ||||
const int num = selection.getNumSelected(); | const int num = selection.getNumSelected(); | ||||
@@ -630,6 +636,17 @@ private: | |||||
} | } | ||||
} | } | ||||
void updateControlPoints() | |||||
{ | |||||
if (! canvas->isControlPointMode()) | |||||
{ | |||||
controlPoints.clear(); | |||||
return; | |||||
} | |||||
canvas->updateControlPointComponents (this, controlPoints); | |||||
} | |||||
void updateMarkers (OwnedArray <MarkerComponent>& markers, const bool isX) | void updateMarkers (OwnedArray <MarkerComponent>& markers, const bool isX) | ||||
{ | { | ||||
MarkerListBase& markerList = canvas->getMarkerList (isX); | MarkerListBase& markerList = canvas->getMarkerList (isX); | ||||
@@ -827,6 +844,21 @@ const Rectangle<int> EditorCanvasBase::objectSpaceToScreenSpace (const Rectangle | |||||
return r + origin; | return r + origin; | ||||
} | } | ||||
void EditorCanvasBase::enableResizingMode() | |||||
{ | |||||
enableControlPointMode (ValueTree::invalid); | |||||
} | |||||
void EditorCanvasBase::enableControlPointMode (const ValueTree& objectToEdit) | |||||
{ | |||||
if (controlPointEditingTarget != objectToEdit) | |||||
{ | |||||
controlPointEditingTarget = objectToEdit; | |||||
getSelection().deselectAll(); | |||||
overlay->update(); | |||||
} | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
void EditorCanvasBase::paint (Graphics& g) | void EditorCanvasBase::paint (Graphics& g) | ||||
{ | { | ||||
@@ -938,6 +970,8 @@ void EditorCanvasBase::endDrag (const MouseEvent& e) | |||||
{ | { | ||||
dragger->drag (e, e.getPosition() - origin); | dragger->drag (e, e.getPosition() - origin); | ||||
dragger = 0; | dragger = 0; | ||||
getUndoManager().beginNewTransaction(); | |||||
} | } | ||||
} | } | ||||
@@ -88,6 +88,7 @@ public: | |||||
virtual void deselectNonDraggableObjects() = 0; | virtual void deselectNonDraggableObjects() = 0; | ||||
virtual void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area) = 0; | virtual void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area) = 0; | ||||
//============================================================================== | |||||
class DragOperation | class DragOperation | ||||
{ | { | ||||
public: | public: | ||||
@@ -105,6 +106,12 @@ public: | |||||
void continueDrag (const MouseEvent& e); | void continueDrag (const MouseEvent& e); | ||||
void endDrag (const MouseEvent& e); | void endDrag (const MouseEvent& e); | ||||
void enableResizingMode(); | |||||
void enableControlPointMode (const ValueTree& objectToEdit); | |||||
bool isResizingMode() const { return ! isControlPointMode(); } | |||||
bool isControlPointMode() const { return controlPointEditingTarget.isValid(); } | |||||
//============================================================================== | //============================================================================== | ||||
Component* getComponentHolder() const { return componentHolder; } | Component* getComponentHolder() const { return componentHolder; } | ||||
EditorPanelBase* getPanel() const; | EditorPanelBase* getPanel() const; | ||||
@@ -129,14 +136,15 @@ public: | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
virtual void updateExtraComponentsForObject (const ValueTree& state, Component* parent, | |||||
OwnedArray<OverlayItemComponent>& existingComps) = 0; | |||||
virtual void updateControlPointComponents (Component* parent, | |||||
OwnedArray<OverlayItemComponent>& existingComps) = 0; | |||||
protected: | protected: | ||||
//============================================================================== | //============================================================================== | ||||
const BorderSize border; | const BorderSize border; | ||||
Point<int> origin; | Point<int> origin; | ||||
double scaleFactor; | double scaleFactor; | ||||
ValueTree controlPointEditingTarget; | |||||
friend class OverlayItemComponent; | friend class OverlayItemComponent; | ||||
class ResizeFrame; | class ResizeFrame; | ||||
@@ -83,7 +83,7 @@ MainWindow::MainWindow() | |||||
// don't want the window to take focus when the title-bar is clicked.. | // don't want the window to take focus when the title-bar is clicked.. | ||||
setWantsKeyboardFocus (false); | setWantsKeyboardFocus (false); | ||||
//getPeer()->setCurrentRenderingEngine (0); | |||||
getPeer()->setCurrentRenderingEngine (0); | |||||
} | } | ||||
MainWindow::~MainWindow() | MainWindow::~MainWindow() | ||||
@@ -26,6 +26,8 @@ | |||||
#ifndef __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | #ifndef __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | ||||
#define __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | #define __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ | ||||
class FillTypeEditorComponent; | |||||
//============================================================================== | //============================================================================== | ||||
class PopupFillSelector : public Component, | class PopupFillSelector : public Component, | ||||
public ChangeListener, | public ChangeListener, | ||||
@@ -33,8 +35,10 @@ class PopupFillSelector : public Component, | |||||
public ButtonListener | public ButtonListener | ||||
{ | { | ||||
public: | public: | ||||
PopupFillSelector (const ValueTree& fillState_, UndoManager* undoManager_) | |||||
: fillState (fillState_), | |||||
PopupFillSelector (const ValueTree& fillState_, const ColourGradient& defaultGradient_, UndoManager* undoManager_) | |||||
: gradientPicker (defaultGradient_), | |||||
defaultGradient (defaultGradient_), | |||||
fillState (fillState_), | |||||
undoManager (undoManager_) | undoManager (undoManager_) | ||||
{ | { | ||||
colourButton.setButtonText ("Colour"); | colourButton.setButtonText ("Colour"); | ||||
@@ -76,15 +80,6 @@ public: | |||||
imageButton.removeButtonListener (this); | imageButton.removeButtonListener (this); | ||||
} | } | ||||
static void showAt (Component* comp, const ValueTree& fill, UndoManager* undoManager) | |||||
{ | |||||
PopupFillSelector popup (fill, undoManager); | |||||
PopupMenu m; | |||||
m.addCustomItem (1234, &popup, 300, 400, false); | |||||
m.showAt (comp); | |||||
} | |||||
void resized() | void resized() | ||||
{ | { | ||||
const int y = 2, w = 80, h = 22; | const int y = 2, w = 80, h = 22; | ||||
@@ -99,18 +94,44 @@ public: | |||||
void buttonClicked (Button* b) | void buttonClicked (Button* b) | ||||
{ | { | ||||
RelativePoint gp1, gp2; | |||||
FillType currentFill (readFillType (&gp1, &gp2)); | |||||
if (b == &colourButton) | if (b == &colourButton) | ||||
{ | { | ||||
setFillType (colourPicker.getCurrentColour()); | |||||
if (! currentFill.isColour()) | |||||
setFillType (colourPicker.getCurrentColour()); | |||||
} | } | ||||
else if (b == &gradientButton) | else if (b == &gradientButton) | ||||
{ | { | ||||
setFillType (gradientPicker.getGradient()); | |||||
if (! currentFill.isGradient()) | |||||
{ | |||||
// Use a cunning trick to make the wrapper dig out the earlier gradient settings, if there are any.. | |||||
FillType newFill (defaultGradient); | |||||
ValueTree temp ("dummy"); | |||||
Drawable::ValueTreeWrapperBase::writeFillType (temp, newFill, 0, 0, 0); | |||||
fillState.setProperty (Drawable::ValueTreeWrapperBase::type, temp [Drawable::ValueTreeWrapperBase::type], undoManager); | |||||
newFill = readFillType (&gp1, &gp2); | |||||
if (newFill.gradient->getNumColours() <= 1) | |||||
{ | |||||
newFill = FillType (defaultGradient); | |||||
Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager); | |||||
} | |||||
else | |||||
{ | |||||
Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, undoManager); | |||||
} | |||||
refresh(); | |||||
} | |||||
} | } | ||||
else if (b == &imageButton) | else if (b == &imageButton) | ||||
{ | { | ||||
setFillType (FillType (*StoredSettings::getInstance()->getFallbackImage(), | |||||
AffineTransform::identity)); | |||||
if (! currentFill.isTiledImage()) | |||||
setFillType (FillType (*StoredSettings::getInstance()->getFallbackImage(), | |||||
AffineTransform::identity)); | |||||
} | } | ||||
} | } | ||||
@@ -122,7 +143,7 @@ public: | |||||
void setFillType (const FillType& newFill) | void setFillType (const FillType& newFill) | ||||
{ | { | ||||
RelativePoint gp1, gp2; | RelativePoint gp1, gp2; | ||||
const FillType currentFill (readFillType (&gp1, &gp2)); | |||||
FillType currentFill (readFillType (&gp1, &gp2)); | |||||
if (currentFill != newFill) | if (currentFill != newFill) | ||||
{ | { | ||||
@@ -146,7 +167,7 @@ public: | |||||
void refresh() | void refresh() | ||||
{ | { | ||||
const FillType newFill (readFillType (0, 0)); | |||||
FillType newFill (readFillType (0, 0)); | |||||
colourPicker.setVisible (newFill.isColour()); | colourPicker.setVisible (newFill.isColour()); | ||||
gradientPicker.setVisible (newFill.isGradient()); | gradientPicker.setVisible (newFill.isGradient()); | ||||
@@ -158,6 +179,12 @@ public: | |||||
} | } | ||||
else if (newFill.isGradient()) | else if (newFill.isGradient()) | ||||
{ | { | ||||
if (newFill.gradient->getNumColours() <= 1) | |||||
{ | |||||
newFill = FillType (defaultGradient); | |||||
Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager); | |||||
} | |||||
gradientButton.setToggleState (true, false); | gradientButton.setToggleState (true, false); | ||||
gradientPicker.setGradient (*newFill.gradient); | gradientPicker.setGradient (*newFill.gradient); | ||||
} | } | ||||
@@ -178,8 +205,8 @@ private: | |||||
private ChangeListener | private ChangeListener | ||||
{ | { | ||||
public: | public: | ||||
GradientDesigner() | |||||
: gradient (Colours::red, 0.0f, 0.0f, Colours::blue, 200.0f, 200.0f, false), | |||||
GradientDesigner (const ColourGradient& gradient_) | |||||
: gradient (gradient_), | |||||
selectedPoint (-1), | selectedPoint (-1), | ||||
dragging (false), | dragging (false), | ||||
draggingNewPoint (false), | draggingNewPoint (false), | ||||
@@ -302,8 +329,10 @@ private: | |||||
void setGradient (const ColourGradient& newGradient) | void setGradient (const ColourGradient& newGradient) | ||||
{ | { | ||||
if (newGradient != gradient) | |||||
if (newGradient != gradient || selectedPoint < 0) | |||||
{ | { | ||||
jassert (newGradient.getNumColours() > 1); | |||||
gradient = newGradient; | gradient = newGradient; | ||||
if (selectedPoint < 0) | if (selectedPoint < 0) | ||||
@@ -375,8 +404,10 @@ private: | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
FillTypeEditorComponent* owner; | |||||
StoredSettings::ColourSelectorWithSwatches colourPicker; | StoredSettings::ColourSelectorWithSwatches colourPicker; | ||||
GradientDesigner gradientPicker; | GradientDesigner gradientPicker; | ||||
ColourGradient defaultGradient; | |||||
ValueTree fillState; | ValueTree fillState; | ||||
UndoManager* undoManager; | UndoManager* undoManager; | ||||
@@ -404,6 +435,8 @@ public: | |||||
{ | { | ||||
} | } | ||||
const ColourGradient getDefaultGradient() const; | |||||
void paint (Graphics& g) | void paint (Graphics& g) | ||||
{ | { | ||||
g.setColour (Colours::grey); | g.setColour (Colours::grey); | ||||
@@ -447,7 +480,12 @@ public: | |||||
void mouseDown (const MouseEvent& e) | void mouseDown (const MouseEvent& e) | ||||
{ | { | ||||
undoManager->beginNewTransaction(); | undoManager->beginNewTransaction(); | ||||
PopupFillSelector::showAt (this, fillState, undoManager); | |||||
PopupFillSelector popup (fillState, getDefaultGradient(), undoManager); | |||||
PopupMenu m; | |||||
m.addCustomItem (1234, &popup, 300, 450, false); | |||||
m.showAt (this); | |||||
} | } | ||||
void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { refresh(); } | void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { refresh(); } | ||||
@@ -462,6 +500,7 @@ private: | |||||
FillType fillType; | FillType fillType; | ||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
class FillTypePropertyComponent : public PropertyComponent | class FillTypePropertyComponent : public PropertyComponent | ||||
{ | { | ||||
@@ -484,6 +523,8 @@ public: | |||||
editor.setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this)); | editor.setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this)); | ||||
} | } | ||||
virtual const ColourGradient getDefaultGradient() = 0; | |||||
void refresh() {} | void refresh() {} | ||||
protected: | protected: | ||||
@@ -24,6 +24,7 @@ | |||||
*/ | */ | ||||
#include "../jucer_Headers.h" | #include "../jucer_Headers.h" | ||||
#include "jucer_FillTypePropertyComponent.h" | |||||
//============================================================================== | //============================================================================== | ||||
@@ -406,3 +407,11 @@ RelativeRectangleLayoutManager::ComponentPosition::ComponentPosition (Component* | |||||
: component (component_), name (name_), coords (coords_) | : component (component_), name (name_), coords (coords_) | ||||
{ | { | ||||
} | } | ||||
//============================================================================== | |||||
const ColourGradient FillTypeEditorComponent::getDefaultGradient() const | |||||
{ | |||||
FillTypePropertyComponent* p = dynamic_cast <FillTypePropertyComponent*> (getParentComponent()); | |||||
jassert (p != 0); | |||||
return p->getDefaultGradient(); | |||||
} |
@@ -16322,6 +16322,18 @@ ValueTree ValueTree::SharedObject::getChildWithName (const Identifier& typeToMat | |||||
return ValueTree::invalid; | return ValueTree::invalid; | ||||
} | } | ||||
ValueTree ValueTree::SharedObject::getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager) | |||||
{ | |||||
for (int i = 0; i < children.size(); ++i) | |||||
if (children.getUnchecked(i)->type == typeToMatch) | |||||
return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i))); | |||||
SharedObject* const newObject = new SharedObject (typeToMatch); | |||||
addChild (newObject, -1, undoManager); | |||||
return ValueTree (newObject); | |||||
} | |||||
ValueTree ValueTree::SharedObject::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ValueTree ValueTree::SharedObject::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ||||
{ | { | ||||
for (int i = 0; i < children.size(); ++i) | for (int i = 0; i < children.size(); ++i) | ||||
@@ -16654,6 +16666,11 @@ ValueTree ValueTree::getChildWithName (const Identifier& type) const | |||||
return object != 0 ? object->getChildWithName (type) : ValueTree::invalid; | return object != 0 ? object->getChildWithName (type) : ValueTree::invalid; | ||||
} | } | ||||
ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager) | |||||
{ | |||||
return object != 0 ? object->getOrCreateChildWithName (type, undoManager) : ValueTree::invalid; | |||||
} | |||||
ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ||||
{ | { | ||||
return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; | return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; | ||||
@@ -78991,7 +79008,7 @@ int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlo | |||||
jassert (point1.getX() != 987654.0f); | jassert (point1.getX() != 987654.0f); | ||||
#endif | #endif | ||||
const int numEntries = jlimit (1, (colours.size() - 1) << 8, | |||||
const int numEntries = jlimit (1, jmax (1, (colours.size() - 1) << 8), | |||||
3 * (int) point1.transformedBy (transform) | 3 * (int) point1.transformedBy (transform) | ||||
.getDistanceFrom (point2.transformedBy (transform))); | .getDistanceFrom (point2.transformedBy (transform))); | ||||
lookupTable.malloc (numEntries); | lookupTable.malloc (numEntries); | ||||
@@ -83946,10 +83963,6 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||||
{ | { | ||||
v.setProperty (type, "solid", undoManager); | v.setProperty (type, "solid", undoManager); | ||||
v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager); | v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager); | ||||
v.removeProperty (gradientPoint1, undoManager); | |||||
v.removeProperty (gradientPoint2, undoManager); | |||||
v.removeProperty (radial, undoManager); | |||||
v.removeProperty (colours, undoManager); | |||||
} | } | ||||
else if (fillType.isGradient()) | else if (fillType.isGradient()) | ||||
{ | { | ||||
@@ -83964,19 +83977,12 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||||
<< " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); | << " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); | ||||
v.setProperty (colours, s.trimStart(), undoManager); | v.setProperty (colours, s.trimStart(), undoManager); | ||||
v.removeProperty (colour, undoManager); | |||||
} | } | ||||
else if (fillType.isTiledImage()) | else if (fillType.isTiledImage()) | ||||
{ | { | ||||
v.setProperty (type, "image", undoManager); | v.setProperty (type, "image", undoManager); | ||||
jassertfalse; //xxx todo | jassertfalse; //xxx todo | ||||
v.removeProperty (gradientPoint1, undoManager); | |||||
v.removeProperty (gradientPoint2, undoManager); | |||||
v.removeProperty (radial, undoManager); | |||||
v.removeProperty (colours, undoManager); | |||||
v.removeProperty (colour, undoManager); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -83984,21 +83990,6 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||||
} | } | ||||
} | } | ||||
void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, const FillType& fillType, | |||||
const RelativePoint* gp1, const RelativePoint* gp2, | |||||
UndoManager* const undoManager) | |||||
{ | |||||
ValueTree v (state.getChildWithName (tag)); | |||||
if (! v.isValid()) | |||||
{ | |||||
state.addChild (ValueTree (tag), -1, undoManager); | |||||
v = state.getChildWithName (tag); | |||||
} | |||||
writeFillType (v, fillType, gp1, gp2, undoManager); | |||||
} | |||||
END_JUCE_NAMESPACE | END_JUCE_NAMESPACE | ||||
/*** End of inlined file: juce_Drawable.cpp ***/ | /*** End of inlined file: juce_Drawable.cpp ***/ | ||||
@@ -84325,12 +84316,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const | |||||
ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager) | ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager) | ||||
{ | { | ||||
const ValueTree childList (getChildList()); | |||||
if (childList.isValid()) | |||||
return childList; | |||||
state.addChild (ValueTree (childGroupTag), 0, undoManager); | |||||
return getChildList(); | |||||
return state.getOrCreateChildWithName (childGroupTag, undoManager); | |||||
} | } | ||||
int DrawableComposite::ValueTreeWrapper::getNumDrawables() const | int DrawableComposite::ValueTreeWrapper::getNumDrawables() const | ||||
@@ -84431,12 +84417,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const | |||||
ValueTree DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager) | ValueTree DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager) | ||||
{ | { | ||||
const ValueTree markerList (getMarkerList (xAxis)); | |||||
if (markerList.isValid()) | |||||
return markerList; | |||||
state.addChild (ValueTree (xAxis ? markerGroupTagX : markerGroupTagY), -1, undoManager); | |||||
return getMarkerList (xAxis); | |||||
return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager); | |||||
} | } | ||||
int DrawableComposite::ValueTreeWrapper::getNumMarkers (bool xAxis) const | int DrawableComposite::ValueTreeWrapper::getNumMarkers (bool xAxis) const | ||||
@@ -84919,6 +84900,8 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre | |||||
|| controlPoints[1] != newControlPoint[1] | || controlPoints[1] != newControlPoint[1] | ||||
|| controlPoints[2] != newControlPoint[2]) | || controlPoints[2] != newControlPoint[2]) | ||||
{ | { | ||||
const Rectangle<float> damage (getBounds()); | |||||
opacity = newOpacity; | opacity = newOpacity; | ||||
overlayColour = newOverlayColour; | overlayColour = newOverlayColour; | ||||
controlPoints[0] = newControlPoint[0]; | controlPoints[0] = newControlPoint[0]; | ||||
@@ -84934,7 +84917,7 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre | |||||
image = newImage; | image = newImage; | ||||
} | } | ||||
return getBounds(); | |||||
return damage.getUnion (getBounds()); | |||||
} | } | ||||
ImageCache::release (newImage); | ImageCache::release (newImage); | ||||
@@ -85118,12 +85101,16 @@ Drawable* DrawablePath::createCopy() const | |||||
const Identifier DrawablePath::valueTreeType ("Path"); | const Identifier DrawablePath::valueTreeType ("Path"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::fill ("fill"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::stroke ("stroke"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::fill ("Fill"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::stroke ("Stroke"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::path ("Path"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::jointStyle ("jointStyle"); | const Identifier DrawablePath::ValueTreeWrapper::jointStyle ("jointStyle"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::capStyle ("capStyle"); | const Identifier DrawablePath::ValueTreeWrapper::capStyle ("capStyle"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::strokeWidth ("strokeWidth"); | const Identifier DrawablePath::ValueTreeWrapper::strokeWidth ("strokeWidth"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::path ("path"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3"); | |||||
DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | ||||
: ValueTreeWrapperBase (state_) | : ValueTreeWrapperBase (state_) | ||||
@@ -85131,6 +85118,11 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | |||||
jassert (state.hasType (valueTreeType)); | jassert (state.hasType (valueTreeType)); | ||||
} | } | ||||
ValueTree DrawablePath::ValueTreeWrapper::getPathState() | |||||
{ | |||||
return state.getOrCreateChildWithName (path, 0); | |||||
} | |||||
ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() | ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() | ||||
{ | { | ||||
ValueTree v (state.getChildWithName (fill)); | ValueTree v (state.getChildWithName (fill)); | ||||
@@ -85159,7 +85151,8 @@ const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate:: | |||||
void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, | void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, | ||||
const RelativePoint* gp2, UndoManager* undoManager) | const RelativePoint* gp2, UndoManager* undoManager) | ||||
{ | { | ||||
replaceFillType (fill, newFill, gp1, gp2, undoManager); | |||||
ValueTree v (state.getOrCreateChildWithName (fill, undoManager)); | |||||
writeFillType (v, newFill, gp1, gp2, undoManager); | |||||
} | } | ||||
const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | ||||
@@ -85170,7 +85163,8 @@ const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate | |||||
void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, | void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, | ||||
const RelativePoint* gp2, UndoManager* undoManager) | const RelativePoint* gp2, UndoManager* undoManager) | ||||
{ | { | ||||
replaceFillType (stroke, newFill, gp1, gp2, undoManager); | |||||
ValueTree v (state.getOrCreateChildWithName (stroke, undoManager)); | |||||
writeFillType (v, newFill, gp1, gp2, undoManager); | |||||
} | } | ||||
const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const | const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const | ||||
@@ -85196,15 +85190,50 @@ void DrawablePath::ValueTreeWrapper::setStrokeType (const PathStrokeType& newStr | |||||
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); | ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); | ||||
} | } | ||||
void DrawablePath::ValueTreeWrapper::getPath (RelativePointPath& p) const | |||||
bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const | |||||
{ | |||||
return state [nonZeroWinding]; | |||||
} | |||||
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager) | |||||
{ | |||||
state.setProperty (nonZeroWinding, b, undoManager); | |||||
} | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); | |||||
DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) | |||||
: state (state_) | |||||
{ | |||||
} | |||||
DrawablePath::ValueTreeWrapper::Element::~Element() | |||||
{ | { | ||||
RelativePointPath newPath (state [path]); | |||||
p.swapWith (newPath); | |||||
} | } | ||||
void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager) | |||||
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw() | |||||
{ | { | ||||
state.setProperty (path, newPath, undoManager); | |||||
const Identifier i (state.getType()); | |||||
if (i == startSubPathElement || i == lineToElement) return 1; | |||||
if (i == quadraticToElement) return 2; | |||||
if (i == cubicToElement) return 3; | |||||
return 0; | |||||
} | |||||
const RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const | |||||
{ | |||||
jassert (index >= 0 && index < getNumControlPoints()); | |||||
return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString()); | |||||
} | |||||
void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager) | |||||
{ | |||||
jassert (index >= 0 && index < getNumControlPoints()); | |||||
return state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager); | |||||
} | } | ||||
const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | ||||
@@ -85232,8 +85261,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||||
const PathStrokeType newStroke (v.getStrokeType()); | const PathStrokeType newStroke (v.getStrokeType()); | ||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath()); | |||||
v.getPath (*newRelativePath); | |||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree)); | |||||
Path newPath; | Path newPath; | ||||
newRelativePath->createPath (newPath, parent); | newRelativePath->createPath (newPath, parent); | ||||
@@ -85268,9 +85296,14 @@ const ValueTree DrawablePath::createValueTree (ImageProvider*) const | |||||
v.setStrokeType (strokeType, 0); | v.setStrokeType (strokeType, 0); | ||||
if (relativePath != 0) | if (relativePath != 0) | ||||
v.setPath (relativePath->toString(), 0); | |||||
{ | |||||
relativePath->writeTo (tree, 0); | |||||
} | |||||
else | else | ||||
v.setPath (path.toString(), 0); | |||||
{ | |||||
RelativePointPath rp (path); | |||||
rp.writeTo (tree, 0); | |||||
} | |||||
return tree; | return tree; | ||||
} | } | ||||
@@ -92784,6 +92817,11 @@ RelativePoint::RelativePoint (const Point<float>& absolutePoint) | |||||
{ | { | ||||
} | } | ||||
RelativePoint::RelativePoint (const float x_, const float y_) | |||||
: x (x_, true), y (y_, false) | |||||
{ | |||||
} | |||||
RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_) | RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_) | ||||
: x (x_), y (y_) | : x (x_), y (y_) | ||||
{ | { | ||||
@@ -92911,88 +92949,83 @@ RelativePointPath::RelativePointPath (const RelativePointPath& other) | |||||
: usesNonZeroWinding (true), | : usesNonZeroWinding (true), | ||||
containsDynamicPoints (false) | containsDynamicPoints (false) | ||||
{ | { | ||||
parseString (other.toString()); | |||||
ValueTree state (DrawablePath::valueTreeType); | |||||
writeTo (state, 0); | |||||
parse (state); | |||||
} | } | ||||
RelativePointPath::RelativePointPath (const String& s) | |||||
RelativePointPath::RelativePointPath (const ValueTree& drawable) | |||||
: usesNonZeroWinding (true), | : usesNonZeroWinding (true), | ||||
containsDynamicPoints (false) | containsDynamicPoints (false) | ||||
{ | { | ||||
parseString (s); | |||||
parse (drawable); | |||||
} | } | ||||
void RelativePointPath::parseString (const String& s) | |||||
RelativePointPath::RelativePointPath (const Path& path) | |||||
{ | { | ||||
int i = 0; | |||||
juce_wchar marker = 'm'; | |||||
int numValues = 2; | |||||
RelativePoint points [3]; | |||||
usesNonZeroWinding = path.isUsingNonZeroWinding(); | |||||
for (;;) | |||||
Path::Iterator i (path); | |||||
while (i.next()) | |||||
{ | { | ||||
RelativeCoordinateHelpers::skipWhitespace (s, i); | |||||
const juce_wchar firstChar = s[i]; | |||||
switch (i.elementType) | |||||
{ | |||||
case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break; | |||||
case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break; | |||||
case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break; | |||||
case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break; | |||||
case Path::Iterator::closePath: elements.add (new CloseSubPath()); break; | |||||
default: jassertfalse; break; | |||||
} | |||||
} | |||||
} | |||||
if (firstChar == 0) | |||||
break; | |||||
void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (state); | |||||
wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager); | |||||
const juce_wchar secondChar = s[i + 1]; | |||||
ValueTree pathTree (wrapper.getPathState()); | |||||
pathTree.removeAllChildren (undoManager); | |||||
if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar)) | |||||
{ | |||||
if (firstChar == 'm' || firstChar == 'l') | |||||
{ | |||||
++i; | |||||
marker = firstChar; | |||||
numValues = 1; | |||||
} | |||||
else if (firstChar == 'q') | |||||
{ | |||||
++i; | |||||
marker = firstChar; | |||||
numValues = 2; | |||||
} | |||||
else if (firstChar == 'c') | |||||
{ | |||||
++i; | |||||
marker = firstChar; | |||||
numValues = 3; | |||||
} | |||||
else if (firstChar == 'z') | |||||
{ | |||||
++i; | |||||
marker = 'm'; | |||||
numValues = 2; | |||||
elements.add (new CloseSubPath()); | |||||
continue; | |||||
} | |||||
else if (firstChar == 'a') | |||||
{ | |||||
++i; | |||||
usesNonZeroWinding = false; | |||||
continue; | |||||
} | |||||
} | |||||
for (int i = 0; i < elements.size(); ++i) | |||||
pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager); | |||||
} | |||||
if (firstChar == '#') | |||||
++i; | |||||
void RelativePointPath::parse (const ValueTree& state) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (state); | |||||
usesNonZeroWinding = wrapper.usesNonZeroWinding(); | |||||
RelativePoint points[3]; | |||||
const ValueTree pathTree (wrapper.getPathState()); | |||||
const int num = pathTree.getNumChildren(); | |||||
for (int i = 0; i < num; ++i) | |||||
{ | |||||
const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); | |||||
for (int j = 0; j < numValues; ++j) | |||||
const int numCps = e.getNumControlPoints(); | |||||
for (int j = 0; j < numCps; ++j) | |||||
{ | { | ||||
const RelativeCoordinate x (RelativeCoordinateHelpers::readNextCoordinate (s, i, true)); | |||||
const RelativeCoordinate y (RelativeCoordinateHelpers::readNextCoordinate (s, i, false)); | |||||
points[j] = RelativePoint (x, y); | |||||
points[j] = e.getControlPoint (j); | |||||
containsDynamicPoints = containsDynamicPoints || points[j].isDynamic(); | containsDynamicPoints = containsDynamicPoints || points[j].isDynamic(); | ||||
} | } | ||||
switch (marker) | |||||
{ | |||||
case 'm': elements.add (new StartSubPath (points[0])); break; | |||||
case 'l': elements.add (new LineTo (points[0])); break; | |||||
case 'q': elements.add (new QuadraticTo (points[0], points[1])); break; | |||||
case 'c': elements.add (new CubicTo (points[0], points[1], points[2])); break; | |||||
default: jassertfalse; break; // illegal string format? | |||||
} | |||||
const Identifier type (e.getType()); | |||||
if (type == DrawablePath::ValueTreeWrapper::Element::startSubPathElement) | |||||
elements.add (new StartSubPath (points[0])); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::closeSubPathElement) | |||||
elements.add (new CloseSubPath()); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::lineToElement) | |||||
elements.add (new LineTo (points[0])); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::quadraticToElement) | |||||
elements.add (new QuadraticTo (points[0], points[1])); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::cubicToElement) | |||||
elements.add (new CubicTo (points[0], points[1], points[2])); | |||||
else | |||||
jassertfalse; | |||||
} | } | ||||
} | } | ||||
@@ -93017,27 +93050,6 @@ bool RelativePointPath::containsAnyDynamicPoints() const | |||||
return containsDynamicPoints; | return containsDynamicPoints; | ||||
} | } | ||||
const String RelativePointPath::toString() const | |||||
{ | |||||
ElementType lastType = nullElement; | |||||
MemoryOutputStream out; | |||||
if (! usesNonZeroWinding) | |||||
out << 'a'; | |||||
for (int i = 0; i < elements.size(); ++i) | |||||
{ | |||||
if (out.getDataSize() > 0) | |||||
out << ' '; | |||||
const ElementBase* const e = elements.getUnchecked(i); | |||||
e->write (out, lastType); | |||||
lastType = e->type; | |||||
} | |||||
return out.toUTF8(); | |||||
} | |||||
RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_) | RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_) | ||||
{ | { | ||||
} | } | ||||
@@ -93047,16 +93059,11 @@ RelativePointPath::StartSubPath::StartSubPath (const RelativePoint& pos) | |||||
{ | { | ||||
} | } | ||||
void RelativePointPath::StartSubPath::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::StartSubPath::createTree() const | |||||
{ | { | ||||
const String p (startPos.toString()); | |||||
if (lastTypeWritten != startSubPathElement) | |||||
out << "m "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) | |||||
out << '#'; | |||||
out << p; | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::startSubPathElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, startPos.toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -93076,10 +93083,9 @@ RelativePointPath::CloseSubPath::CloseSubPath() | |||||
{ | { | ||||
} | } | ||||
void RelativePointPath::CloseSubPath::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::CloseSubPath::createTree() const | |||||
{ | { | ||||
if (lastTypeWritten != closeSubPathElement) | |||||
out << 'z'; | |||||
return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement); | |||||
} | } | ||||
void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const | void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const | ||||
@@ -93098,16 +93104,11 @@ RelativePointPath::LineTo::LineTo (const RelativePoint& endPoint_) | |||||
{ | { | ||||
} | } | ||||
void RelativePointPath::LineTo::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::LineTo::createTree() const | |||||
{ | { | ||||
const String p (endPoint.toString()); | |||||
if (lastTypeWritten != lineToElement) | |||||
out << "l "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) | |||||
out << '#'; | |||||
out << p; | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::lineToElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, endPoint.toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -93129,16 +93130,12 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint, | |||||
controlPoints[1] = endPoint; | controlPoints[1] = endPoint; | ||||
} | } | ||||
void RelativePointPath::QuadraticTo::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::QuadraticTo::createTree() const | |||||
{ | { | ||||
const String p1 (controlPoints[0].toString()); | |||||
if (lastTypeWritten != quadraticToElement) | |||||
out << "q "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) | |||||
out << '#'; | |||||
out << p1 << ' ' << controlPoints[1].toString(); | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::quadraticToElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -93162,16 +93159,13 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R | |||||
controlPoints[2] = endPoint; | controlPoints[2] = endPoint; | ||||
} | } | ||||
void RelativePointPath::CubicTo::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::CubicTo::createTree() const | |||||
{ | { | ||||
const String p1 (controlPoints[0].toString()); | |||||
if (lastTypeWritten != cubicToElement) | |||||
out << "c "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) | |||||
out << '#'; | |||||
out << p1 << ' ' << controlPoints[1].toString() << ' ' << controlPoints[2].toString(); | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::cubicToElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point3, controlPoints[2].toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -64,7 +64,7 @@ | |||||
*/ | */ | ||||
#define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
#define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
#define JUCE_BUILDNUMBER 7 | |||||
#define JUCE_BUILDNUMBER 8 | |||||
/** Current Juce version number. | /** Current Juce version number. | ||||
@@ -13094,12 +13094,22 @@ public: | |||||
*/ | */ | ||||
ValueTree getChild (int index) const; | ValueTree getChild (int index) const; | ||||
/** Looks for a child node with the speficied type name. | |||||
/** Returns the first child node with the speficied type name. | |||||
If no such node is found, it'll return an invalid node. (See isValid() to find out | If no such node is found, it'll return an invalid node. (See isValid() to find out | ||||
whether a node is valid). | whether a node is valid). | ||||
@see getOrCreateChildWithName | |||||
*/ | */ | ||||
ValueTree getChildWithName (const Identifier& type) const; | ValueTree getChildWithName (const Identifier& type) const; | ||||
/** Returns the first child node with the speficied type name, creating and adding | |||||
a child with this name if there wasn't already one there. | |||||
The only time this will return an invalid object is when the object that you're calling | |||||
the method on is itself invalid. | |||||
@see getChildWithName | |||||
*/ | |||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); | |||||
/** Looks for the first child node that has the speficied property value. | /** Looks for the first child node that has the speficied property value. | ||||
This will scan the child nodes in order, until it finds one that has property that matches | This will scan the child nodes in order, until it finds one that has property that matches | ||||
@@ -13342,6 +13352,7 @@ private: | |||||
bool isAChildOf (const SharedObject* possibleParent) const; | bool isAChildOf (const SharedObject* possibleParent) const; | ||||
int indexOf (const ValueTree& child) const; | int indexOf (const ValueTree& child) const; | ||||
ValueTree getChildWithName (const Identifier& type) const; | ValueTree getChildWithName (const Identifier& type) const; | ||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); | |||||
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const; | ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const; | ||||
void addChild (SharedObject* child, int index, UndoManager*); | void addChild (SharedObject* child, int index, UndoManager*); | ||||
void removeChild (int childIndex, UndoManager*); | void removeChild (int childIndex, UndoManager*); | ||||
@@ -42124,6 +42135,9 @@ public: | |||||
/** Creates an absolute point, relative to the origin. */ | /** Creates an absolute point, relative to the origin. */ | ||||
RelativePoint (const Point<float>& absolutePoint); | RelativePoint (const Point<float>& absolutePoint); | ||||
/** Creates an absolute point, relative to the origin. */ | |||||
RelativePoint (float absoluteX, float absoluteY); | |||||
/** Creates an absolute point from two coordinates. */ | /** Creates an absolute point from two coordinates. */ | ||||
RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y); | RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y); | ||||
@@ -42246,7 +42260,8 @@ public: | |||||
RelativePointPath(); | RelativePointPath(); | ||||
RelativePointPath (const RelativePointPath& other); | RelativePointPath (const RelativePointPath& other); | ||||
RelativePointPath (const String& stringVersion); | |||||
RelativePointPath (const ValueTree& drawable); | |||||
RelativePointPath (const Path& path); | |||||
~RelativePointPath(); | ~RelativePointPath(); | ||||
/** Resolves this points in this path and adds them to a normal Path object. */ | /** Resolves this points in this path and adds them to a normal Path object. */ | ||||
@@ -42255,11 +42270,8 @@ public: | |||||
/** Returns true if the path contains any non-fixed points. */ | /** Returns true if the path contains any non-fixed points. */ | ||||
bool containsAnyDynamicPoints() const; | bool containsAnyDynamicPoints() const; | ||||
/** Returns a string version of the path. | |||||
This has the same format as Path::toString(), but since it can contain RelativeCoordinate | |||||
positions, it can't be parsed by the Path class if any of the points are dynamic. | |||||
*/ | |||||
const String toString() const; | |||||
/** Writes the path to this drawable encoding. */ | |||||
void writeTo (ValueTree state, UndoManager* undoManager); | |||||
/** Quickly swaps the contents of this path with another. */ | /** Quickly swaps the contents of this path with another. */ | ||||
void swapWith (RelativePointPath& other) throw(); | void swapWith (RelativePointPath& other) throw(); | ||||
@@ -42284,7 +42296,7 @@ public: | |||||
public: | public: | ||||
ElementBase (ElementType type); | ElementBase (ElementType type); | ||||
virtual ~ElementBase() {} | virtual ~ElementBase() {} | ||||
virtual void write (OutputStream& out, ElementType lastTypeWritten) const = 0; | |||||
virtual const ValueTree createTree() const = 0; | |||||
virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0; | virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0; | ||||
virtual RelativePoint* getControlPoints (int& numPoints) = 0; | virtual RelativePoint* getControlPoints (int& numPoints) = 0; | ||||
@@ -42300,7 +42312,7 @@ public: | |||||
public: | public: | ||||
StartSubPath (const RelativePoint& pos); | StartSubPath (const RelativePoint& pos); | ||||
~StartSubPath() {} | ~StartSubPath() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -42316,7 +42328,7 @@ public: | |||||
public: | public: | ||||
CloseSubPath(); | CloseSubPath(); | ||||
~CloseSubPath() {} | ~CloseSubPath() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -42330,7 +42342,7 @@ public: | |||||
public: | public: | ||||
LineTo (const RelativePoint& endPoint); | LineTo (const RelativePoint& endPoint); | ||||
~LineTo() {} | ~LineTo() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -42346,7 +42358,7 @@ public: | |||||
public: | public: | ||||
QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); | QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); | ||||
~QuadraticTo() {} | ~QuadraticTo() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -42362,7 +42374,7 @@ public: | |||||
public: | public: | ||||
CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); | CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); | ||||
~CubicTo() {} | ~CubicTo() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -42379,7 +42391,7 @@ public: | |||||
private: | private: | ||||
bool containsDynamicPoints; | bool containsDynamicPoints; | ||||
void parseString (const String& s); | |||||
void parse (const ValueTree& state); | |||||
RelativePointPath& operator= (const RelativePointPath&); | RelativePointPath& operator= (const RelativePointPath&); | ||||
}; | }; | ||||
@@ -42601,13 +42613,8 @@ public: | |||||
const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | ||||
UndoManager* undoManager); | UndoManager* undoManager); | ||||
protected: | |||||
ValueTree state; | ValueTree state; | ||||
static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; | static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; | ||||
void replaceFillType (const Identifier& tag, const FillType& fillType, | |||||
const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | |||||
UndoManager* undoManager); | |||||
}; | }; | ||||
juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
@@ -58599,7 +58606,7 @@ public: | |||||
/** @internal */ | /** @internal */ | ||||
static const Identifier valueTreeType; | static const Identifier valueTreeType; | ||||
/** @internal */ | /** @internal */ | ||||
const Identifier getValueTreeType() const { return valueTreeType; } | |||||
const Identifier getValueTreeType() const { return valueTreeType; } | |||||
/** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */ | /** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */ | ||||
class ValueTreeWrapper : public ValueTreeWrapperBase | class ValueTreeWrapper : public ValueTreeWrapperBase | ||||
@@ -58620,10 +58627,31 @@ public: | |||||
const PathStrokeType getStrokeType() const; | const PathStrokeType getStrokeType() const; | ||||
void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); | void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); | ||||
void getPath (RelativePointPath& path) const; | |||||
void setPath (const String& newPath, UndoManager* undoManager); | |||||
bool usesNonZeroWinding() const; | |||||
void setUsesNonZeroWinding (bool b, UndoManager* undoManager); | |||||
class Element | |||||
{ | |||||
public: | |||||
explicit Element (const ValueTree& state); | |||||
~Element(); | |||||
const Identifier getType() const throw() { return state.getType(); } | |||||
int getNumControlPoints() const throw(); | |||||
const RelativePoint getControlPoint (int index) const; | |||||
void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager); | |||||
static const Identifier startSubPathElement, closeSubPathElement, | |||||
lineToElement, quadraticToElement, cubicToElement; | |||||
ValueTree state; | |||||
}; | |||||
ValueTree getPathState(); | |||||
static const Identifier fill, stroke, jointStyle, capStyle, strokeWidth, path; | |||||
static const Identifier fill, stroke, path, jointStyle, capStyle, strokeWidth, | |||||
nonZeroWinding, point1, point2, point3; | |||||
}; | }; | ||||
juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
@@ -381,6 +381,18 @@ ValueTree ValueTree::SharedObject::getChildWithName (const Identifier& typeToMat | |||||
return ValueTree::invalid; | return ValueTree::invalid; | ||||
} | } | ||||
ValueTree ValueTree::SharedObject::getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager) | |||||
{ | |||||
for (int i = 0; i < children.size(); ++i) | |||||
if (children.getUnchecked(i)->type == typeToMatch) | |||||
return ValueTree (static_cast <SharedObject*> (children.getUnchecked(i))); | |||||
SharedObject* const newObject = new SharedObject (typeToMatch); | |||||
addChild (newObject, -1, undoManager); | |||||
return ValueTree (newObject); | |||||
} | |||||
ValueTree ValueTree::SharedObject::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ValueTree ValueTree::SharedObject::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ||||
{ | { | ||||
for (int i = 0; i < children.size(); ++i) | for (int i = 0; i < children.size(); ++i) | ||||
@@ -717,6 +729,11 @@ ValueTree ValueTree::getChildWithName (const Identifier& type) const | |||||
return object != 0 ? object->getChildWithName (type) : ValueTree::invalid; | return object != 0 ? object->getChildWithName (type) : ValueTree::invalid; | ||||
} | } | ||||
ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager) | |||||
{ | |||||
return object != 0 ? object->getOrCreateChildWithName (type, undoManager) : ValueTree::invalid; | |||||
} | |||||
ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const | ||||
{ | { | ||||
return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; | return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; | ||||
@@ -214,12 +214,22 @@ public: | |||||
*/ | */ | ||||
ValueTree getChild (int index) const; | ValueTree getChild (int index) const; | ||||
/** Looks for a child node with the speficied type name. | |||||
/** Returns the first child node with the speficied type name. | |||||
If no such node is found, it'll return an invalid node. (See isValid() to find out | If no such node is found, it'll return an invalid node. (See isValid() to find out | ||||
whether a node is valid). | whether a node is valid). | ||||
@see getOrCreateChildWithName | |||||
*/ | */ | ||||
ValueTree getChildWithName (const Identifier& type) const; | ValueTree getChildWithName (const Identifier& type) const; | ||||
/** Returns the first child node with the speficied type name, creating and adding | |||||
a child with this name if there wasn't already one there. | |||||
The only time this will return an invalid object is when the object that you're calling | |||||
the method on is itself invalid. | |||||
@see getChildWithName | |||||
*/ | |||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); | |||||
/** Looks for the first child node that has the speficied property value. | /** Looks for the first child node that has the speficied property value. | ||||
This will scan the child nodes in order, until it finds one that has property that matches | This will scan the child nodes in order, until it finds one that has property that matches | ||||
@@ -467,6 +477,7 @@ private: | |||||
bool isAChildOf (const SharedObject* possibleParent) const; | bool isAChildOf (const SharedObject* possibleParent) const; | ||||
int indexOf (const ValueTree& child) const; | int indexOf (const ValueTree& child) const; | ||||
ValueTree getChildWithName (const Identifier& type) const; | ValueTree getChildWithName (const Identifier& type) const; | ||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); | |||||
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const; | ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const; | ||||
void addChild (SharedObject* child, int index, UndoManager*); | void addChild (SharedObject* child, int index, UndoManager*); | ||||
void removeChild (int childIndex, UndoManager*); | void removeChild (int childIndex, UndoManager*); | ||||
@@ -33,7 +33,7 @@ | |||||
*/ | */ | ||||
#define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
#define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
#define JUCE_BUILDNUMBER 7 | |||||
#define JUCE_BUILDNUMBER 8 | |||||
/** Current Juce version number. | /** Current Juce version number. | ||||
@@ -160,7 +160,7 @@ int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlo | |||||
jassert (point1.getX() != 987654.0f); | jassert (point1.getX() != 987654.0f); | ||||
#endif | #endif | ||||
const int numEntries = jlimit (1, (colours.size() - 1) << 8, | |||||
const int numEntries = jlimit (1, jmax (1, (colours.size() - 1) << 8), | |||||
3 * (int) point1.transformedBy (transform) | 3 * (int) point1.transformedBy (transform) | ||||
.getDistanceFrom (point2.transformedBy (transform))); | .getDistanceFrom (point2.transformedBy (transform))); | ||||
lookupTable.malloc (numEntries); | lookupTable.malloc (numEntries); | ||||
@@ -238,10 +238,6 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||||
{ | { | ||||
v.setProperty (type, "solid", undoManager); | v.setProperty (type, "solid", undoManager); | ||||
v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager); | v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), undoManager); | ||||
v.removeProperty (gradientPoint1, undoManager); | |||||
v.removeProperty (gradientPoint2, undoManager); | |||||
v.removeProperty (radial, undoManager); | |||||
v.removeProperty (colours, undoManager); | |||||
} | } | ||||
else if (fillType.isGradient()) | else if (fillType.isGradient()) | ||||
{ | { | ||||
@@ -256,19 +252,12 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||||
<< " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); | << " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); | ||||
v.setProperty (colours, s.trimStart(), undoManager); | v.setProperty (colours, s.trimStart(), undoManager); | ||||
v.removeProperty (colour, undoManager); | |||||
} | } | ||||
else if (fillType.isTiledImage()) | else if (fillType.isTiledImage()) | ||||
{ | { | ||||
v.setProperty (type, "image", undoManager); | v.setProperty (type, "image", undoManager); | ||||
jassertfalse; //xxx todo | jassertfalse; //xxx todo | ||||
v.removeProperty (gradientPoint1, undoManager); | |||||
v.removeProperty (gradientPoint2, undoManager); | |||||
v.removeProperty (radial, undoManager); | |||||
v.removeProperty (colours, undoManager); | |||||
v.removeProperty (colour, undoManager); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -276,20 +265,5 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType | |||||
} | } | ||||
} | } | ||||
void Drawable::ValueTreeWrapperBase::replaceFillType (const Identifier& tag, const FillType& fillType, | |||||
const RelativePoint* gp1, const RelativePoint* gp2, | |||||
UndoManager* const undoManager) | |||||
{ | |||||
ValueTree v (state.getChildWithName (tag)); | |||||
if (! v.isValid()) | |||||
{ | |||||
state.addChild (ValueTree (tag), -1, undoManager); | |||||
v = state.getChildWithName (tag); | |||||
} | |||||
writeFillType (v, fillType, gp1, gp2, undoManager); | |||||
} | |||||
END_JUCE_NAMESPACE | END_JUCE_NAMESPACE |
@@ -255,13 +255,8 @@ public: | |||||
const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | ||||
UndoManager* undoManager); | UndoManager* undoManager); | ||||
protected: | |||||
ValueTree state; | ValueTree state; | ||||
static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; | static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; | ||||
void replaceFillType (const Identifier& tag, const FillType& fillType, | |||||
const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, | |||||
UndoManager* undoManager); | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -360,12 +360,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const | |||||
ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager) | ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager) | ||||
{ | { | ||||
const ValueTree childList (getChildList()); | |||||
if (childList.isValid()) | |||||
return childList; | |||||
state.addChild (ValueTree (childGroupTag), 0, undoManager); | |||||
return getChildList(); | |||||
return state.getOrCreateChildWithName (childGroupTag, undoManager); | |||||
} | } | ||||
int DrawableComposite::ValueTreeWrapper::getNumDrawables() const | int DrawableComposite::ValueTreeWrapper::getNumDrawables() const | ||||
@@ -466,12 +461,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const | |||||
ValueTree DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager) | ValueTree DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager) | ||||
{ | { | ||||
const ValueTree markerList (getMarkerList (xAxis)); | |||||
if (markerList.isValid()) | |||||
return markerList; | |||||
state.addChild (ValueTree (xAxis ? markerGroupTagX : markerGroupTagY), -1, undoManager); | |||||
return getMarkerList (xAxis); | |||||
return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager); | |||||
} | } | ||||
int DrawableComposite::ValueTreeWrapper::getNumMarkers (bool xAxis) const | int DrawableComposite::ValueTreeWrapper::getNumMarkers (bool xAxis) const | ||||
@@ -313,6 +313,8 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre | |||||
|| controlPoints[1] != newControlPoint[1] | || controlPoints[1] != newControlPoint[1] | ||||
|| controlPoints[2] != newControlPoint[2]) | || controlPoints[2] != newControlPoint[2]) | ||||
{ | { | ||||
const Rectangle<float> damage (getBounds()); | |||||
opacity = newOpacity; | opacity = newOpacity; | ||||
overlayColour = newOverlayColour; | overlayColour = newOverlayColour; | ||||
controlPoints[0] = newControlPoint[0]; | controlPoints[0] = newControlPoint[0]; | ||||
@@ -328,7 +330,7 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre | |||||
image = newImage; | image = newImage; | ||||
} | } | ||||
return getBounds(); | |||||
return damage.getUnion (getBounds()); | |||||
} | } | ||||
ImageCache::release (newImage); | ImageCache::release (newImage); | ||||
@@ -183,12 +183,16 @@ Drawable* DrawablePath::createCopy() const | |||||
//============================================================================== | //============================================================================== | ||||
const Identifier DrawablePath::valueTreeType ("Path"); | const Identifier DrawablePath::valueTreeType ("Path"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::fill ("fill"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::stroke ("stroke"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::fill ("Fill"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::stroke ("Stroke"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::path ("Path"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::jointStyle ("jointStyle"); | const Identifier DrawablePath::ValueTreeWrapper::jointStyle ("jointStyle"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::capStyle ("capStyle"); | const Identifier DrawablePath::ValueTreeWrapper::capStyle ("capStyle"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::strokeWidth ("strokeWidth"); | const Identifier DrawablePath::ValueTreeWrapper::strokeWidth ("strokeWidth"); | ||||
const Identifier DrawablePath::ValueTreeWrapper::path ("path"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3"); | |||||
//============================================================================== | //============================================================================== | ||||
DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | ||||
@@ -197,6 +201,11 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) | |||||
jassert (state.hasType (valueTreeType)); | jassert (state.hasType (valueTreeType)); | ||||
} | } | ||||
ValueTree DrawablePath::ValueTreeWrapper::getPathState() | |||||
{ | |||||
return state.getOrCreateChildWithName (path, 0); | |||||
} | |||||
ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() | ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() | ||||
{ | { | ||||
ValueTree v (state.getChildWithName (fill)); | ValueTree v (state.getChildWithName (fill)); | ||||
@@ -225,7 +234,8 @@ const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate:: | |||||
void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, | void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, | ||||
const RelativePoint* gp2, UndoManager* undoManager) | const RelativePoint* gp2, UndoManager* undoManager) | ||||
{ | { | ||||
replaceFillType (fill, newFill, gp1, gp2, undoManager); | |||||
ValueTree v (state.getOrCreateChildWithName (fill, undoManager)); | |||||
writeFillType (v, newFill, gp1, gp2, undoManager); | |||||
} | } | ||||
const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const | ||||
@@ -236,7 +246,8 @@ const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate | |||||
void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, | void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, | ||||
const RelativePoint* gp2, UndoManager* undoManager) | const RelativePoint* gp2, UndoManager* undoManager) | ||||
{ | { | ||||
replaceFillType (stroke, newFill, gp1, gp2, undoManager); | |||||
ValueTree v (state.getOrCreateChildWithName (stroke, undoManager)); | |||||
writeFillType (v, newFill, gp1, gp2, undoManager); | |||||
} | } | ||||
const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const | const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const | ||||
@@ -262,17 +273,54 @@ void DrawablePath::ValueTreeWrapper::setStrokeType (const PathStrokeType& newStr | |||||
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); | ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); | ||||
} | } | ||||
void DrawablePath::ValueTreeWrapper::getPath (RelativePointPath& p) const | |||||
bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const | |||||
{ | |||||
return state [nonZeroWinding]; | |||||
} | |||||
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager) | |||||
{ | |||||
state.setProperty (nonZeroWinding, b, undoManager); | |||||
} | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); | |||||
const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); | |||||
DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) | |||||
: state (state_) | |||||
{ | |||||
} | |||||
DrawablePath::ValueTreeWrapper::Element::~Element() | |||||
{ | { | ||||
RelativePointPath newPath (state [path]); | |||||
p.swapWith (newPath); | |||||
} | } | ||||
void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager) | |||||
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw() | |||||
{ | { | ||||
state.setProperty (path, newPath, undoManager); | |||||
const Identifier i (state.getType()); | |||||
if (i == startSubPathElement || i == lineToElement) return 1; | |||||
if (i == quadraticToElement) return 2; | |||||
if (i == cubicToElement) return 3; | |||||
return 0; | |||||
} | } | ||||
const RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const | |||||
{ | |||||
jassert (index >= 0 && index < getNumControlPoints()); | |||||
return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString()); | |||||
} | |||||
void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager) | |||||
{ | |||||
jassert (index >= 0 && index < getNumControlPoints()); | |||||
return state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager); | |||||
} | |||||
//============================================================================== | |||||
const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*) | ||||
{ | { | ||||
Rectangle<float> damageRect; | Rectangle<float> damageRect; | ||||
@@ -298,8 +346,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree | |||||
const PathStrokeType newStroke (v.getStrokeType()); | const PathStrokeType newStroke (v.getStrokeType()); | ||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath()); | |||||
v.getPath (*newRelativePath); | |||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree)); | |||||
Path newPath; | Path newPath; | ||||
newRelativePath->createPath (newPath, parent); | newRelativePath->createPath (newPath, parent); | ||||
@@ -334,9 +381,14 @@ const ValueTree DrawablePath::createValueTree (ImageProvider*) const | |||||
v.setStrokeType (strokeType, 0); | v.setStrokeType (strokeType, 0); | ||||
if (relativePath != 0) | if (relativePath != 0) | ||||
v.setPath (relativePath->toString(), 0); | |||||
{ | |||||
relativePath->writeTo (tree, 0); | |||||
} | |||||
else | else | ||||
v.setPath (path.toString(), 0); | |||||
{ | |||||
RelativePointPath rp (path); | |||||
rp.writeTo (tree, 0); | |||||
} | |||||
return tree; | return tree; | ||||
} | } | ||||
@@ -119,7 +119,7 @@ public: | |||||
/** @internal */ | /** @internal */ | ||||
static const Identifier valueTreeType; | static const Identifier valueTreeType; | ||||
/** @internal */ | /** @internal */ | ||||
const Identifier getValueTreeType() const { return valueTreeType; } | |||||
const Identifier getValueTreeType() const { return valueTreeType; } | |||||
//============================================================================== | //============================================================================== | ||||
/** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */ | /** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */ | ||||
@@ -141,10 +141,31 @@ public: | |||||
const PathStrokeType getStrokeType() const; | const PathStrokeType getStrokeType() const; | ||||
void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); | void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); | ||||
void getPath (RelativePointPath& path) const; | |||||
void setPath (const String& newPath, UndoManager* undoManager); | |||||
bool usesNonZeroWinding() const; | |||||
void setUsesNonZeroWinding (bool b, UndoManager* undoManager); | |||||
static const Identifier fill, stroke, jointStyle, capStyle, strokeWidth, path; | |||||
class Element | |||||
{ | |||||
public: | |||||
explicit Element (const ValueTree& state); | |||||
~Element(); | |||||
const Identifier getType() const throw() { return state.getType(); } | |||||
int getNumControlPoints() const throw(); | |||||
const RelativePoint getControlPoint (int index) const; | |||||
void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager); | |||||
static const Identifier startSubPathElement, closeSubPathElement, | |||||
lineToElement, quadraticToElement, cubicToElement; | |||||
ValueTree state; | |||||
}; | |||||
ValueTree getPathState(); | |||||
static const Identifier fill, stroke, path, jointStyle, capStyle, strokeWidth, | |||||
nonZeroWinding, point1, point2, point3; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -28,6 +28,7 @@ | |||||
BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
#include "juce_RelativeCoordinate.h" | #include "juce_RelativeCoordinate.h" | ||||
#include "../drawables/juce_DrawablePath.h" | |||||
#include "../../../io/streams/juce_MemoryOutputStream.h" | #include "../../../io/streams/juce_MemoryOutputStream.h" | ||||
@@ -494,6 +495,11 @@ RelativePoint::RelativePoint (const Point<float>& absolutePoint) | |||||
{ | { | ||||
} | } | ||||
RelativePoint::RelativePoint (const float x_, const float y_) | |||||
: x (x_, true), y (y_, false) | |||||
{ | |||||
} | |||||
RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_) | RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_) | ||||
: x (x_), y (y_) | : x (x_), y (y_) | ||||
{ | { | ||||
@@ -625,88 +631,83 @@ RelativePointPath::RelativePointPath (const RelativePointPath& other) | |||||
: usesNonZeroWinding (true), | : usesNonZeroWinding (true), | ||||
containsDynamicPoints (false) | containsDynamicPoints (false) | ||||
{ | { | ||||
parseString (other.toString()); | |||||
ValueTree state (DrawablePath::valueTreeType); | |||||
writeTo (state, 0); | |||||
parse (state); | |||||
} | } | ||||
RelativePointPath::RelativePointPath (const String& s) | |||||
RelativePointPath::RelativePointPath (const ValueTree& drawable) | |||||
: usesNonZeroWinding (true), | : usesNonZeroWinding (true), | ||||
containsDynamicPoints (false) | containsDynamicPoints (false) | ||||
{ | { | ||||
parseString (s); | |||||
parse (drawable); | |||||
} | } | ||||
void RelativePointPath::parseString (const String& s) | |||||
RelativePointPath::RelativePointPath (const Path& path) | |||||
{ | { | ||||
int i = 0; | |||||
juce_wchar marker = 'm'; | |||||
int numValues = 2; | |||||
RelativePoint points [3]; | |||||
usesNonZeroWinding = path.isUsingNonZeroWinding(); | |||||
for (;;) | |||||
Path::Iterator i (path); | |||||
while (i.next()) | |||||
{ | { | ||||
RelativeCoordinateHelpers::skipWhitespace (s, i); | |||||
const juce_wchar firstChar = s[i]; | |||||
switch (i.elementType) | |||||
{ | |||||
case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break; | |||||
case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break; | |||||
case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break; | |||||
case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break; | |||||
case Path::Iterator::closePath: elements.add (new CloseSubPath()); break; | |||||
default: jassertfalse; break; | |||||
} | |||||
} | |||||
} | |||||
if (firstChar == 0) | |||||
break; | |||||
void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (state); | |||||
wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager); | |||||
const juce_wchar secondChar = s[i + 1]; | |||||
ValueTree pathTree (wrapper.getPathState()); | |||||
pathTree.removeAllChildren (undoManager); | |||||
if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar)) | |||||
{ | |||||
if (firstChar == 'm' || firstChar == 'l') | |||||
{ | |||||
++i; | |||||
marker = firstChar; | |||||
numValues = 1; | |||||
} | |||||
else if (firstChar == 'q') | |||||
{ | |||||
++i; | |||||
marker = firstChar; | |||||
numValues = 2; | |||||
} | |||||
else if (firstChar == 'c') | |||||
{ | |||||
++i; | |||||
marker = firstChar; | |||||
numValues = 3; | |||||
} | |||||
else if (firstChar == 'z') | |||||
{ | |||||
++i; | |||||
marker = 'm'; | |||||
numValues = 2; | |||||
elements.add (new CloseSubPath()); | |||||
continue; | |||||
} | |||||
else if (firstChar == 'a') | |||||
{ | |||||
++i; | |||||
usesNonZeroWinding = false; | |||||
continue; | |||||
} | |||||
} | |||||
for (int i = 0; i < elements.size(); ++i) | |||||
pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager); | |||||
} | |||||
if (firstChar == '#') | |||||
++i; | |||||
void RelativePointPath::parse (const ValueTree& state) | |||||
{ | |||||
DrawablePath::ValueTreeWrapper wrapper (state); | |||||
usesNonZeroWinding = wrapper.usesNonZeroWinding(); | |||||
RelativePoint points[3]; | |||||
for (int j = 0; j < numValues; ++j) | |||||
const ValueTree pathTree (wrapper.getPathState()); | |||||
const int num = pathTree.getNumChildren(); | |||||
for (int i = 0; i < num; ++i) | |||||
{ | |||||
const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); | |||||
const int numCps = e.getNumControlPoints(); | |||||
for (int j = 0; j < numCps; ++j) | |||||
{ | { | ||||
const RelativeCoordinate x (RelativeCoordinateHelpers::readNextCoordinate (s, i, true)); | |||||
const RelativeCoordinate y (RelativeCoordinateHelpers::readNextCoordinate (s, i, false)); | |||||
points[j] = RelativePoint (x, y); | |||||
points[j] = e.getControlPoint (j); | |||||
containsDynamicPoints = containsDynamicPoints || points[j].isDynamic(); | containsDynamicPoints = containsDynamicPoints || points[j].isDynamic(); | ||||
} | } | ||||
switch (marker) | |||||
{ | |||||
case 'm': elements.add (new StartSubPath (points[0])); break; | |||||
case 'l': elements.add (new LineTo (points[0])); break; | |||||
case 'q': elements.add (new QuadraticTo (points[0], points[1])); break; | |||||
case 'c': elements.add (new CubicTo (points[0], points[1], points[2])); break; | |||||
default: jassertfalse; break; // illegal string format? | |||||
} | |||||
const Identifier type (e.getType()); | |||||
if (type == DrawablePath::ValueTreeWrapper::Element::startSubPathElement) | |||||
elements.add (new StartSubPath (points[0])); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::closeSubPathElement) | |||||
elements.add (new CloseSubPath()); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::lineToElement) | |||||
elements.add (new LineTo (points[0])); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::quadraticToElement) | |||||
elements.add (new QuadraticTo (points[0], points[1])); | |||||
else if (type == DrawablePath::ValueTreeWrapper::Element::cubicToElement) | |||||
elements.add (new CubicTo (points[0], points[1], points[2])); | |||||
else | |||||
jassertfalse; | |||||
} | } | ||||
} | } | ||||
@@ -731,27 +732,6 @@ bool RelativePointPath::containsAnyDynamicPoints() const | |||||
return containsDynamicPoints; | return containsDynamicPoints; | ||||
} | } | ||||
const String RelativePointPath::toString() const | |||||
{ | |||||
ElementType lastType = nullElement; | |||||
MemoryOutputStream out; | |||||
if (! usesNonZeroWinding) | |||||
out << 'a'; | |||||
for (int i = 0; i < elements.size(); ++i) | |||||
{ | |||||
if (out.getDataSize() > 0) | |||||
out << ' '; | |||||
const ElementBase* const e = elements.getUnchecked(i); | |||||
e->write (out, lastType); | |||||
lastType = e->type; | |||||
} | |||||
return out.toUTF8(); | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_) | RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_) | ||||
{ | { | ||||
@@ -763,16 +743,11 @@ RelativePointPath::StartSubPath::StartSubPath (const RelativePoint& pos) | |||||
{ | { | ||||
} | } | ||||
void RelativePointPath::StartSubPath::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::StartSubPath::createTree() const | |||||
{ | { | ||||
const String p (startPos.toString()); | |||||
if (lastTypeWritten != startSubPathElement) | |||||
out << "m "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) | |||||
out << '#'; | |||||
out << p; | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::startSubPathElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, startPos.toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -793,10 +768,9 @@ RelativePointPath::CloseSubPath::CloseSubPath() | |||||
{ | { | ||||
} | } | ||||
void RelativePointPath::CloseSubPath::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::CloseSubPath::createTree() const | |||||
{ | { | ||||
if (lastTypeWritten != closeSubPathElement) | |||||
out << 'z'; | |||||
return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement); | |||||
} | } | ||||
void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const | void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const | ||||
@@ -816,16 +790,11 @@ RelativePointPath::LineTo::LineTo (const RelativePoint& endPoint_) | |||||
{ | { | ||||
} | } | ||||
void RelativePointPath::LineTo::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::LineTo::createTree() const | |||||
{ | { | ||||
const String p (endPoint.toString()); | |||||
if (lastTypeWritten != lineToElement) | |||||
out << "l "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) | |||||
out << '#'; | |||||
out << p; | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::lineToElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, endPoint.toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -848,16 +817,12 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint, | |||||
controlPoints[1] = endPoint; | controlPoints[1] = endPoint; | ||||
} | } | ||||
void RelativePointPath::QuadraticTo::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::QuadraticTo::createTree() const | |||||
{ | { | ||||
const String p1 (controlPoints[0].toString()); | |||||
if (lastTypeWritten != quadraticToElement) | |||||
out << "q "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) | |||||
out << '#'; | |||||
out << p1 << ' ' << controlPoints[1].toString(); | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::quadraticToElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -882,16 +847,13 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R | |||||
controlPoints[2] = endPoint; | controlPoints[2] = endPoint; | ||||
} | } | ||||
void RelativePointPath::CubicTo::write (OutputStream& out, ElementType lastTypeWritten) const | |||||
const ValueTree RelativePointPath::CubicTo::createTree() const | |||||
{ | { | ||||
const String p1 (controlPoints[0].toString()); | |||||
if (lastTypeWritten != cubicToElement) | |||||
out << "c "; | |||||
else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) | |||||
out << '#'; | |||||
out << p1 << ' ' << controlPoints[1].toString() << ' ' << controlPoints[2].toString(); | |||||
ValueTree v (DrawablePath::ValueTreeWrapper::Element::cubicToElement); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); | |||||
v.setProperty (DrawablePath::ValueTreeWrapper::point3, controlPoints[2].toString(), 0); | |||||
return v; | |||||
} | } | ||||
void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const | ||||
@@ -29,6 +29,7 @@ | |||||
#include "juce_Path.h" | #include "juce_Path.h" | ||||
#include "juce_Rectangle.h" | #include "juce_Rectangle.h" | ||||
#include "../../../containers/juce_OwnedArray.h" | #include "../../../containers/juce_OwnedArray.h" | ||||
#include "../../../containers/juce_ValueTree.h" | |||||
//============================================================================== | //============================================================================== | ||||
@@ -289,6 +290,9 @@ public: | |||||
/** Creates an absolute point, relative to the origin. */ | /** Creates an absolute point, relative to the origin. */ | ||||
RelativePoint (const Point<float>& absolutePoint); | RelativePoint (const Point<float>& absolutePoint); | ||||
/** Creates an absolute point, relative to the origin. */ | |||||
RelativePoint (float absoluteX, float absoluteY); | |||||
/** Creates an absolute point from two coordinates. */ | /** Creates an absolute point from two coordinates. */ | ||||
RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y); | RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y); | ||||
@@ -416,7 +420,8 @@ public: | |||||
//============================================================================== | //============================================================================== | ||||
RelativePointPath(); | RelativePointPath(); | ||||
RelativePointPath (const RelativePointPath& other); | RelativePointPath (const RelativePointPath& other); | ||||
RelativePointPath (const String& stringVersion); | |||||
RelativePointPath (const ValueTree& drawable); | |||||
RelativePointPath (const Path& path); | |||||
~RelativePointPath(); | ~RelativePointPath(); | ||||
//============================================================================== | //============================================================================== | ||||
@@ -426,11 +431,8 @@ public: | |||||
/** Returns true if the path contains any non-fixed points. */ | /** Returns true if the path contains any non-fixed points. */ | ||||
bool containsAnyDynamicPoints() const; | bool containsAnyDynamicPoints() const; | ||||
/** Returns a string version of the path. | |||||
This has the same format as Path::toString(), but since it can contain RelativeCoordinate | |||||
positions, it can't be parsed by the Path class if any of the points are dynamic. | |||||
*/ | |||||
const String toString() const; | |||||
/** Writes the path to this drawable encoding. */ | |||||
void writeTo (ValueTree state, UndoManager* undoManager); | |||||
/** Quickly swaps the contents of this path with another. */ | /** Quickly swaps the contents of this path with another. */ | ||||
void swapWith (RelativePointPath& other) throw(); | void swapWith (RelativePointPath& other) throw(); | ||||
@@ -457,7 +459,7 @@ public: | |||||
public: | public: | ||||
ElementBase (ElementType type); | ElementBase (ElementType type); | ||||
virtual ~ElementBase() {} | virtual ~ElementBase() {} | ||||
virtual void write (OutputStream& out, ElementType lastTypeWritten) const = 0; | |||||
virtual const ValueTree createTree() const = 0; | |||||
virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0; | virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0; | ||||
virtual RelativePoint* getControlPoints (int& numPoints) = 0; | virtual RelativePoint* getControlPoints (int& numPoints) = 0; | ||||
@@ -473,7 +475,7 @@ public: | |||||
public: | public: | ||||
StartSubPath (const RelativePoint& pos); | StartSubPath (const RelativePoint& pos); | ||||
~StartSubPath() {} | ~StartSubPath() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -489,7 +491,7 @@ public: | |||||
public: | public: | ||||
CloseSubPath(); | CloseSubPath(); | ||||
~CloseSubPath() {} | ~CloseSubPath() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -503,7 +505,7 @@ public: | |||||
public: | public: | ||||
LineTo (const RelativePoint& endPoint); | LineTo (const RelativePoint& endPoint); | ||||
~LineTo() {} | ~LineTo() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -519,7 +521,7 @@ public: | |||||
public: | public: | ||||
QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); | QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); | ||||
~QuadraticTo() {} | ~QuadraticTo() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -535,7 +537,7 @@ public: | |||||
public: | public: | ||||
CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); | CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); | ||||
~CubicTo() {} | ~CubicTo() {} | ||||
void write (OutputStream& out, ElementType lastTypeWritten) const; | |||||
const ValueTree createTree() const; | |||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; | ||||
RelativePoint* getControlPoints (int& numPoints); | RelativePoint* getControlPoints (int& numPoints); | ||||
@@ -553,7 +555,7 @@ public: | |||||
private: | private: | ||||
bool containsDynamicPoints; | bool containsDynamicPoints; | ||||
void parseString (const String& s); | |||||
void parse (const ValueTree& state); | |||||
RelativePointPath& operator= (const RelativePointPath&); | RelativePointPath& operator= (const RelativePointPath&); | ||||
}; | }; | ||||
@@ -282,7 +282,7 @@ public: | |||||
timeOutMs); | timeOutMs); | ||||
if (responseHeaders != 0) | if (responseHeaders != 0) | ||||
juce_getInternetFileHeaders (handle, *responseHeaders); | |||||
juce_getInternetFileHeaders (handle, *responseHeaders); | |||||
} | } | ||||
~WebInputStream() | ~WebInputStream() | ||||
@@ -1,481 +1,481 @@ | |||||
/* | |||||
============================================================================== | |||||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
Copyright 2004-10 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. | |||||
============================================================================== | |||||
*/ | |||||
// (This file gets included by juce_linux_NativeCode.cpp, rather than being | |||||
// compiled on its own). | |||||
#if JUCE_INCLUDED_FILE | |||||
//============================================================================== | |||||
int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) | |||||
{ | |||||
int numResults = 0; | |||||
const int s = socket (AF_INET, SOCK_DGRAM, 0); | |||||
if (s != -1) | |||||
{ | |||||
char buf [1024]; | |||||
struct ifconf ifc; | |||||
ifc.ifc_len = sizeof (buf); | |||||
ifc.ifc_buf = buf; | |||||
ioctl (s, SIOCGIFCONF, &ifc); | |||||
for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) | |||||
{ | |||||
struct ifreq ifr; | |||||
strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); | |||||
if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 | |||||
&& (ifr.ifr_flags & IFF_LOOPBACK) == 0 | |||||
&& ioctl (s, SIOCGIFHWADDR, &ifr) == 0 | |||||
&& numResults < maxNum) | |||||
{ | |||||
int64 a = 0; | |||||
for (int j = 6; --j >= 0;) | |||||
a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data [littleEndian ? j : (5 - j)]; | |||||
*addresses++ = a; | |||||
++numResults; | |||||
} | |||||
} | |||||
close (s); | |||||
} | |||||
return numResults; | |||||
} | |||||
bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
const String& emailSubject, | |||||
const String& bodyText, | |||||
const StringArray& filesToAttach) | |||||
{ | |||||
jassertfalse; // xxx todo | |||||
return false; | |||||
} | |||||
//============================================================================== | |||||
/** A HTTP input stream that uses sockets. | |||||
*/ | |||||
class JUCE_HTTPSocketStream | |||||
{ | |||||
public: | |||||
//============================================================================== | |||||
JUCE_HTTPSocketStream() | |||||
: readPosition (0), | |||||
socketHandle (-1), | |||||
levelsOfRedirection (0), | |||||
timeoutSeconds (15) | |||||
{ | |||||
} | |||||
~JUCE_HTTPSocketStream() | |||||
{ | |||||
closeSocket(); | |||||
} | |||||
//============================================================================== | |||||
bool open (const String& url, | |||||
const String& headers, | |||||
const MemoryBlock& postData, | |||||
const bool isPost, | |||||
URL::OpenStreamProgressCallback* callback, | |||||
void* callbackContext, | |||||
int timeOutMs) | |||||
{ | |||||
closeSocket(); | |||||
uint32 timeOutTime = Time::getMillisecondCounter(); | |||||
if (timeOutMs == 0) | |||||
timeOutTime += 60000; | |||||
else if (timeOutMs < 0) | |||||
timeOutTime = 0xffffffff; | |||||
else | |||||
timeOutTime += timeOutMs; | |||||
String hostName, hostPath; | |||||
int hostPort; | |||||
if (! decomposeURL (url, hostName, hostPath, hostPort)) | |||||
return false; | |||||
const struct hostent* host = 0; | |||||
int port = 0; | |||||
String proxyName, proxyPath; | |||||
int proxyPort = 0; | |||||
String proxyURL (getenv ("http_proxy")); | |||||
if (proxyURL.startsWithIgnoreCase ("http://")) | |||||
{ | |||||
if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||||
return false; | |||||
host = gethostbyname (proxyName.toUTF8()); | |||||
port = proxyPort; | |||||
} | |||||
else | |||||
{ | |||||
host = gethostbyname (hostName.toUTF8()); | |||||
port = hostPort; | |||||
} | |||||
if (host == 0) | |||||
return false; | |||||
struct sockaddr_in address; | |||||
zerostruct (address); | |||||
memcpy (&address.sin_addr, host->h_addr, host->h_length); | |||||
address.sin_family = host->h_addrtype; | |||||
address.sin_port = htons (port); | |||||
socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); | |||||
if (socketHandle == -1) | |||||
return false; | |||||
int receiveBufferSize = 16384; | |||||
setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||||
setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||||
#if JUCE_MAC | |||||
setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||||
#endif | |||||
if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, | |||||
proxyName, proxyPort, | |||||
hostPath, url, | |||||
headers, postData, | |||||
isPost)); | |||||
size_t totalHeaderSent = 0; | |||||
while (totalHeaderSent < requestHeader.getSize()) | |||||
{ | |||||
if (Time::getMillisecondCounter() > timeOutTime) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | |||||
if (send (socketHandle, | |||||
((const char*) requestHeader.getData()) + totalHeaderSent, | |||||
numToSend, 0) | |||||
!= numToSend) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
totalHeaderSent += numToSend; | |||||
if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
} | |||||
const String responseHeader (readResponse (timeOutTime)); | |||||
if (responseHeader.isNotEmpty()) | |||||
{ | |||||
//DBG (responseHeader); | |||||
headerLines.clear(); | |||||
headerLines.addLines (responseHeader); | |||||
const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||||
.substring (0, 3).getIntValue(); | |||||
//int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||||
//bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||||
String location (findHeaderItem (headerLines, "Location:")); | |||||
if (statusCode >= 300 && statusCode < 400 | |||||
&& location.isNotEmpty()) | |||||
{ | |||||
if (! location.startsWithIgnoreCase ("http://")) | |||||
location = "http://" + location; | |||||
if (levelsOfRedirection++ < 3) | |||||
return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); | |||||
} | |||||
else | |||||
{ | |||||
levelsOfRedirection = 0; | |||||
return true; | |||||
} | |||||
} | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
//============================================================================== | |||||
int read (void* buffer, int bytesToRead) | |||||
{ | |||||
fd_set readbits; | |||||
FD_ZERO (&readbits); | |||||
FD_SET (socketHandle, &readbits); | |||||
struct timeval tv; | |||||
tv.tv_sec = timeoutSeconds; | |||||
tv.tv_usec = 0; | |||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
return 0; // (timeout) | |||||
const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||||
readPosition += bytesRead; | |||||
return bytesRead; | |||||
} | |||||
//============================================================================== | |||||
int readPosition; | |||||
StringArray headerLines; | |||||
//============================================================================== | |||||
juce_UseDebuggingNewOperator | |||||
private: | |||||
int socketHandle, levelsOfRedirection; | |||||
const int timeoutSeconds; | |||||
//============================================================================== | |||||
void closeSocket() | |||||
{ | |||||
if (socketHandle >= 0) | |||||
close (socketHandle); | |||||
socketHandle = -1; | |||||
} | |||||
const MemoryBlock createRequestHeader (const String& hostName, | |||||
const int hostPort, | |||||
const String& proxyName, | |||||
const int proxyPort, | |||||
const String& hostPath, | |||||
const String& originalURL, | |||||
const String& headers, | |||||
const MemoryBlock& postData, | |||||
const bool isPost) | |||||
{ | |||||
String header (isPost ? "POST " : "GET "); | |||||
if (proxyName.isEmpty()) | |||||
{ | |||||
header << hostPath << " HTTP/1.0\r\nHost: " | |||||
<< hostName << ':' << hostPort; | |||||
} | |||||
else | |||||
{ | |||||
header << originalURL << " HTTP/1.0\r\nHost: " | |||||
<< proxyName << ':' << proxyPort; | |||||
} | |||||
header << "\r\nUser-Agent: JUCE/" | |||||
<< JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||||
<< "\r\nConnection: Close\r\nContent-Length: " | |||||
<< postData.getSize() << "\r\n" | |||||
<< headers << "\r\n"; | |||||
MemoryBlock mb; | |||||
mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||||
mb.append (postData.getData(), postData.getSize()); | |||||
return mb; | |||||
} | |||||
const String readResponse (const uint32 timeOutTime) | |||||
{ | |||||
int bytesRead = 0, numConsecutiveLFs = 0; | |||||
MemoryBlock buffer (1024, true); | |||||
while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||||
&& Time::getMillisecondCounter() <= timeOutTime) | |||||
{ | |||||
fd_set readbits; | |||||
FD_ZERO (&readbits); | |||||
FD_SET (socketHandle, &readbits); | |||||
struct timeval tv; | |||||
tv.tv_sec = timeoutSeconds; | |||||
tv.tv_usec = 0; | |||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
return String::empty; // (timeout) | |||||
buffer.ensureSize (bytesRead + 8, true); | |||||
char* const dest = (char*) buffer.getData() + bytesRead; | |||||
if (recv (socketHandle, dest, 1, 0) == -1) | |||||
return String::empty; | |||||
const char lastByte = *dest; | |||||
++bytesRead; | |||||
if (lastByte == '\n') | |||||
++numConsecutiveLFs; | |||||
else if (lastByte != '\r') | |||||
numConsecutiveLFs = 0; | |||||
} | |||||
const String header (String::fromUTF8 ((const char*) buffer.getData())); | |||||
if (header.startsWithIgnoreCase ("HTTP/")) | |||||
return header.trimEnd(); | |||||
return String::empty; | |||||
} | |||||
//============================================================================== | |||||
static bool decomposeURL (const String& url, | |||||
String& host, String& path, int& port) | |||||
{ | |||||
if (! url.startsWithIgnoreCase ("http://")) | |||||
return false; | |||||
const int nextSlash = url.indexOfChar (7, '/'); | |||||
int nextColon = url.indexOfChar (7, ':'); | |||||
if (nextColon > nextSlash && nextSlash > 0) | |||||
nextColon = -1; | |||||
if (nextColon >= 0) | |||||
{ | |||||
host = url.substring (7, nextColon); | |||||
if (nextSlash >= 0) | |||||
port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||||
else | |||||
port = url.substring (nextColon + 1).getIntValue(); | |||||
} | |||||
else | |||||
{ | |||||
port = 80; | |||||
if (nextSlash >= 0) | |||||
host = url.substring (7, nextSlash); | |||||
else | |||||
host = url.substring (7); | |||||
} | |||||
if (nextSlash >= 0) | |||||
path = url.substring (nextSlash); | |||||
else | |||||
path = "/"; | |||||
return true; | |||||
} | |||||
//============================================================================== | |||||
static const String findHeaderItem (const StringArray& lines, const String& itemName) | |||||
{ | |||||
for (int i = 0; i < lines.size(); ++i) | |||||
if (lines[i].startsWithIgnoreCase (itemName)) | |||||
return lines[i].substring (itemName.length()).trim(); | |||||
return String::empty; | |||||
} | |||||
}; | |||||
//============================================================================== | |||||
void* juce_openInternetFile (const String& url, | |||||
const String& headers, | |||||
const MemoryBlock& postData, | |||||
const bool isPost, | |||||
URL::OpenStreamProgressCallback* callback, | |||||
void* callbackContext, | |||||
int timeOutMs) | |||||
{ | |||||
ScopedPointer<JUCE_HTTPSocketStream> s (new JUCE_HTTPSocketStream()); | |||||
if (s->open (url, headers, postData, isPost, callback, callbackContext, timeOutMs)) | |||||
return s.release(); | |||||
return 0; | |||||
} | |||||
void juce_closeInternetFile (void* handle) | |||||
{ | |||||
delete static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
} | |||||
int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
return s != 0 ? s->read (buffer, bytesToRead) : 0; | |||||
} | |||||
int64 juce_getInternetFileContentLength (void* handle) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
if (s != 0) | |||||
{ | |||||
//xxx todo | |||||
jassertfalse | |||||
} | |||||
return -1; | |||||
} | |||||
bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
if (s != 0) | |||||
{ | |||||
for (int i = 0; i < s->headerLines.size(); ++i) | |||||
{ | |||||
const String& headersEntry = s->headerLines[i]; | |||||
const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); | |||||
const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); | |||||
const String previousValue (headers [key]); | |||||
headers.set (key, previousValue.isEmpty() ? value : (previousValue + ";" + value)); | |||||
} | |||||
} | |||||
} | |||||
int juce_seekInInternetFile (void* handle, int newPosition) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
return s != 0 ? s->readPosition : 0; | |||||
} | |||||
#endif | |||||
/* | |||||
============================================================================== | |||||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||||
Copyright 2004-10 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. | |||||
============================================================================== | |||||
*/ | |||||
// (This file gets included by juce_linux_NativeCode.cpp, rather than being | |||||
// compiled on its own). | |||||
#if JUCE_INCLUDED_FILE | |||||
//============================================================================== | |||||
int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) | |||||
{ | |||||
int numResults = 0; | |||||
const int s = socket (AF_INET, SOCK_DGRAM, 0); | |||||
if (s != -1) | |||||
{ | |||||
char buf [1024]; | |||||
struct ifconf ifc; | |||||
ifc.ifc_len = sizeof (buf); | |||||
ifc.ifc_buf = buf; | |||||
ioctl (s, SIOCGIFCONF, &ifc); | |||||
for (unsigned int i = 0; i < ifc.ifc_len / sizeof (struct ifreq); ++i) | |||||
{ | |||||
struct ifreq ifr; | |||||
strcpy (ifr.ifr_name, ifc.ifc_req[i].ifr_name); | |||||
if (ioctl (s, SIOCGIFFLAGS, &ifr) == 0 | |||||
&& (ifr.ifr_flags & IFF_LOOPBACK) == 0 | |||||
&& ioctl (s, SIOCGIFHWADDR, &ifr) == 0 | |||||
&& numResults < maxNum) | |||||
{ | |||||
int64 a = 0; | |||||
for (int j = 6; --j >= 0;) | |||||
a = (a << 8) | (uint8) ifr.ifr_hwaddr.sa_data [littleEndian ? j : (5 - j)]; | |||||
*addresses++ = a; | |||||
++numResults; | |||||
} | |||||
} | |||||
close (s); | |||||
} | |||||
return numResults; | |||||
} | |||||
bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||||
const String& emailSubject, | |||||
const String& bodyText, | |||||
const StringArray& filesToAttach) | |||||
{ | |||||
jassertfalse; // xxx todo | |||||
return false; | |||||
} | |||||
//============================================================================== | |||||
/** A HTTP input stream that uses sockets. | |||||
*/ | |||||
class JUCE_HTTPSocketStream | |||||
{ | |||||
public: | |||||
//============================================================================== | |||||
JUCE_HTTPSocketStream() | |||||
: readPosition (0), | |||||
socketHandle (-1), | |||||
levelsOfRedirection (0), | |||||
timeoutSeconds (15) | |||||
{ | |||||
} | |||||
~JUCE_HTTPSocketStream() | |||||
{ | |||||
closeSocket(); | |||||
} | |||||
//============================================================================== | |||||
bool open (const String& url, | |||||
const String& headers, | |||||
const MemoryBlock& postData, | |||||
const bool isPost, | |||||
URL::OpenStreamProgressCallback* callback, | |||||
void* callbackContext, | |||||
int timeOutMs) | |||||
{ | |||||
closeSocket(); | |||||
uint32 timeOutTime = Time::getMillisecondCounter(); | |||||
if (timeOutMs == 0) | |||||
timeOutTime += 60000; | |||||
else if (timeOutMs < 0) | |||||
timeOutTime = 0xffffffff; | |||||
else | |||||
timeOutTime += timeOutMs; | |||||
String hostName, hostPath; | |||||
int hostPort; | |||||
if (! decomposeURL (url, hostName, hostPath, hostPort)) | |||||
return false; | |||||
const struct hostent* host = 0; | |||||
int port = 0; | |||||
String proxyName, proxyPath; | |||||
int proxyPort = 0; | |||||
String proxyURL (getenv ("http_proxy")); | |||||
if (proxyURL.startsWithIgnoreCase ("http://")) | |||||
{ | |||||
if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) | |||||
return false; | |||||
host = gethostbyname (proxyName.toUTF8()); | |||||
port = proxyPort; | |||||
} | |||||
else | |||||
{ | |||||
host = gethostbyname (hostName.toUTF8()); | |||||
port = hostPort; | |||||
} | |||||
if (host == 0) | |||||
return false; | |||||
struct sockaddr_in address; | |||||
zerostruct (address); | |||||
memcpy (&address.sin_addr, host->h_addr, host->h_length); | |||||
address.sin_family = host->h_addrtype; | |||||
address.sin_port = htons (port); | |||||
socketHandle = socket (host->h_addrtype, SOCK_STREAM, 0); | |||||
if (socketHandle == -1) | |||||
return false; | |||||
int receiveBufferSize = 16384; | |||||
setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize)); | |||||
setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, 0, 0); | |||||
#if JUCE_MAC | |||||
setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0); | |||||
#endif | |||||
if (connect (socketHandle, (struct sockaddr*) &address, sizeof (address)) == -1) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, | |||||
proxyName, proxyPort, | |||||
hostPath, url, | |||||
headers, postData, | |||||
isPost)); | |||||
size_t totalHeaderSent = 0; | |||||
while (totalHeaderSent < requestHeader.getSize()) | |||||
{ | |||||
if (Time::getMillisecondCounter() > timeOutTime) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
const int numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent)); | |||||
if (send (socketHandle, | |||||
((const char*) requestHeader.getData()) + totalHeaderSent, | |||||
numToSend, 0) | |||||
!= numToSend) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
totalHeaderSent += numToSend; | |||||
if (callback != 0 && ! callback (callbackContext, totalHeaderSent, requestHeader.getSize())) | |||||
{ | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
} | |||||
const String responseHeader (readResponse (timeOutTime)); | |||||
if (responseHeader.isNotEmpty()) | |||||
{ | |||||
//DBG (responseHeader); | |||||
headerLines.clear(); | |||||
headerLines.addLines (responseHeader); | |||||
const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||||
.substring (0, 3).getIntValue(); | |||||
//int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||||
//bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||||
String location (findHeaderItem (headerLines, "Location:")); | |||||
if (statusCode >= 300 && statusCode < 400 | |||||
&& location.isNotEmpty()) | |||||
{ | |||||
if (! location.startsWithIgnoreCase ("http://")) | |||||
location = "http://" + location; | |||||
if (levelsOfRedirection++ < 3) | |||||
return open (location, headers, postData, isPost, callback, callbackContext, timeOutMs); | |||||
} | |||||
else | |||||
{ | |||||
levelsOfRedirection = 0; | |||||
return true; | |||||
} | |||||
} | |||||
closeSocket(); | |||||
return false; | |||||
} | |||||
//============================================================================== | |||||
int read (void* buffer, int bytesToRead) | |||||
{ | |||||
fd_set readbits; | |||||
FD_ZERO (&readbits); | |||||
FD_SET (socketHandle, &readbits); | |||||
struct timeval tv; | |||||
tv.tv_sec = timeoutSeconds; | |||||
tv.tv_usec = 0; | |||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
return 0; // (timeout) | |||||
const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); | |||||
readPosition += bytesRead; | |||||
return bytesRead; | |||||
} | |||||
//============================================================================== | |||||
int readPosition; | |||||
StringArray headerLines; | |||||
//============================================================================== | |||||
juce_UseDebuggingNewOperator | |||||
private: | |||||
int socketHandle, levelsOfRedirection; | |||||
const int timeoutSeconds; | |||||
//============================================================================== | |||||
void closeSocket() | |||||
{ | |||||
if (socketHandle >= 0) | |||||
close (socketHandle); | |||||
socketHandle = -1; | |||||
} | |||||
const MemoryBlock createRequestHeader (const String& hostName, | |||||
const int hostPort, | |||||
const String& proxyName, | |||||
const int proxyPort, | |||||
const String& hostPath, | |||||
const String& originalURL, | |||||
const String& headers, | |||||
const MemoryBlock& postData, | |||||
const bool isPost) | |||||
{ | |||||
String header (isPost ? "POST " : "GET "); | |||||
if (proxyName.isEmpty()) | |||||
{ | |||||
header << hostPath << " HTTP/1.0\r\nHost: " | |||||
<< hostName << ':' << hostPort; | |||||
} | |||||
else | |||||
{ | |||||
header << originalURL << " HTTP/1.0\r\nHost: " | |||||
<< proxyName << ':' << proxyPort; | |||||
} | |||||
header << "\r\nUser-Agent: JUCE/" | |||||
<< JUCE_MAJOR_VERSION << '.' << JUCE_MINOR_VERSION | |||||
<< "\r\nConnection: Close\r\nContent-Length: " | |||||
<< postData.getSize() << "\r\n" | |||||
<< headers << "\r\n"; | |||||
MemoryBlock mb; | |||||
mb.append (header.toUTF8(), (int) strlen (header.toUTF8())); | |||||
mb.append (postData.getData(), postData.getSize()); | |||||
return mb; | |||||
} | |||||
const String readResponse (const uint32 timeOutTime) | |||||
{ | |||||
int bytesRead = 0, numConsecutiveLFs = 0; | |||||
MemoryBlock buffer (1024, true); | |||||
while (numConsecutiveLFs < 2 && bytesRead < 32768 | |||||
&& Time::getMillisecondCounter() <= timeOutTime) | |||||
{ | |||||
fd_set readbits; | |||||
FD_ZERO (&readbits); | |||||
FD_SET (socketHandle, &readbits); | |||||
struct timeval tv; | |||||
tv.tv_sec = timeoutSeconds; | |||||
tv.tv_usec = 0; | |||||
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) | |||||
return String::empty; // (timeout) | |||||
buffer.ensureSize (bytesRead + 8, true); | |||||
char* const dest = (char*) buffer.getData() + bytesRead; | |||||
if (recv (socketHandle, dest, 1, 0) == -1) | |||||
return String::empty; | |||||
const char lastByte = *dest; | |||||
++bytesRead; | |||||
if (lastByte == '\n') | |||||
++numConsecutiveLFs; | |||||
else if (lastByte != '\r') | |||||
numConsecutiveLFs = 0; | |||||
} | |||||
const String header (String::fromUTF8 ((const char*) buffer.getData())); | |||||
if (header.startsWithIgnoreCase ("HTTP/")) | |||||
return header.trimEnd(); | |||||
return String::empty; | |||||
} | |||||
//============================================================================== | |||||
static bool decomposeURL (const String& url, | |||||
String& host, String& path, int& port) | |||||
{ | |||||
if (! url.startsWithIgnoreCase ("http://")) | |||||
return false; | |||||
const int nextSlash = url.indexOfChar (7, '/'); | |||||
int nextColon = url.indexOfChar (7, ':'); | |||||
if (nextColon > nextSlash && nextSlash > 0) | |||||
nextColon = -1; | |||||
if (nextColon >= 0) | |||||
{ | |||||
host = url.substring (7, nextColon); | |||||
if (nextSlash >= 0) | |||||
port = url.substring (nextColon + 1, nextSlash).getIntValue(); | |||||
else | |||||
port = url.substring (nextColon + 1).getIntValue(); | |||||
} | |||||
else | |||||
{ | |||||
port = 80; | |||||
if (nextSlash >= 0) | |||||
host = url.substring (7, nextSlash); | |||||
else | |||||
host = url.substring (7); | |||||
} | |||||
if (nextSlash >= 0) | |||||
path = url.substring (nextSlash); | |||||
else | |||||
path = "/"; | |||||
return true; | |||||
} | |||||
//============================================================================== | |||||
static const String findHeaderItem (const StringArray& lines, const String& itemName) | |||||
{ | |||||
for (int i = 0; i < lines.size(); ++i) | |||||
if (lines[i].startsWithIgnoreCase (itemName)) | |||||
return lines[i].substring (itemName.length()).trim(); | |||||
return String::empty; | |||||
} | |||||
}; | |||||
//============================================================================== | |||||
void* juce_openInternetFile (const String& url, | |||||
const String& headers, | |||||
const MemoryBlock& postData, | |||||
const bool isPost, | |||||
URL::OpenStreamProgressCallback* callback, | |||||
void* callbackContext, | |||||
int timeOutMs) | |||||
{ | |||||
ScopedPointer<JUCE_HTTPSocketStream> s (new JUCE_HTTPSocketStream()); | |||||
if (s->open (url, headers, postData, isPost, callback, callbackContext, timeOutMs)) | |||||
return s.release(); | |||||
return 0; | |||||
} | |||||
void juce_closeInternetFile (void* handle) | |||||
{ | |||||
delete static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
} | |||||
int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
return s != 0 ? s->read (buffer, bytesToRead) : 0; | |||||
} | |||||
int64 juce_getInternetFileContentLength (void* handle) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
if (s != 0) | |||||
{ | |||||
//xxx todo | |||||
jassertfalse | |||||
} | |||||
return -1; | |||||
} | |||||
bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
if (s != 0) | |||||
{ | |||||
for (int i = 0; i < s->headerLines.size(); ++i) | |||||
{ | |||||
const String& headersEntry = s->headerLines[i]; | |||||
const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false)); | |||||
const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false)); | |||||
const String previousValue (headers [key]); | |||||
headers.set (key, previousValue.isEmpty() ? value : (previousValue + ";" + value)); | |||||
} | |||||
} | |||||
} | |||||
int juce_seekInInternetFile (void* handle, int newPosition) | |||||
{ | |||||
JUCE_HTTPSocketStream* const s = static_cast <JUCE_HTTPSocketStream*> (handle); | |||||
return s != 0 ? s->readPosition : 0; | |||||
} | |||||
#endif |