mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
More changes to Drawables + Jucer development.
This commit is contained in:
parent
8e1b74a8fc
commit
0c541cfba2
32 changed files with 1868 additions and 1306 deletions
|
|
@ -47,6 +47,7 @@ OBJECTS := \
|
|||
$(OBJDIR)/jucer_ComponentDocument.o \
|
||||
$(OBJDIR)/jucer_ComponentTypeManager.o \
|
||||
$(OBJDIR)/jucer_DrawableDocument.o \
|
||||
$(OBJDIR)/jucer_DrawableTypeHandler.o \
|
||||
$(OBJDIR)/jucer_NewFileWizard.o \
|
||||
$(OBJDIR)/jucer_Project.o \
|
||||
$(OBJDIR)/jucer_ProjectExporter.o \
|
||||
|
|
@ -113,6 +114,11 @@ $(OBJDIR)/jucer_DrawableDocument.o: ../../Source/model/Drawable/jucer_DrawableDo
|
|||
@echo "Compiling jucer_DrawableDocument.cpp"
|
||||
@$(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
|
||||
-@mkdir -p $(OBJDIR)
|
||||
@echo "Compiling jucer_NewFileWizard.cpp"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
061C981D0E9FB70DE5A3D32C = { isa = PBXBuildFile; fileRef = 829C5DA65FB46B99756428C5; };
|
||||
FEDBCD721085272D356BBAEB = { isa = PBXBuildFile; fileRef = D08D9DB99315F76093CF6EE6; };
|
||||
268807D7091702D29033CC27 = { isa = PBXBuildFile; fileRef = B1471E8698D193FBCF0DD13D; };
|
||||
91CCD0421DDD432C1C4903D4 = { isa = PBXBuildFile; fileRef = 330A51CC68AED92F7F5BEC15; };
|
||||
06838545183964D8C1E60530 = { isa = PBXBuildFile; fileRef = 9DCB32BBD7053ACCB598CE79; };
|
||||
048D33BDE7413EFE05D09901 = { isa = PBXBuildFile; fileRef = 4179D4C7BF616A4A3C3E11CA; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
|
|
@ -204,6 +206,7 @@
|
|||
E70E3BE796E91EA86CFE10FE = { isa = PBXGroup; children = (
|
||||
B1471E8698D193FBCF0DD13D,
|
||||
739F94CA6213B43D867AB0FD,
|
||||
330A51CC68AED92F7F5BEC15,
|
||||
BDE8CD9273E1B0D0E500D283 ); name = Drawable; sourceTree = "<group>"; };
|
||||
ADA17383E5554BCDF567AACC = { isa = PBXGroup; children = (
|
||||
9DCB32BBD7053ACCB598CE79,
|
||||
|
|
@ -416,6 +419,7 @@
|
|||
061C981D0E9FB70DE5A3D32C,
|
||||
FEDBCD721085272D356BBAEB,
|
||||
268807D7091702D29033CC27,
|
||||
91CCD0421DDD432C1C4903D4,
|
||||
06838545183964D8C1E60530,
|
||||
048D33BDE7413EFE05D09901,
|
||||
9DA1913A62297D7111E0A6C9,
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@
|
|||
<Filter Name="Drawable">
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.cpp"/>
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.h"/>
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.cpp"/>
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.h"/>
|
||||
</Filter>
|
||||
<Filter Name="Project">
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@
|
|||
<Filter Name="Drawable">
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.cpp"/>
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableDocument.h"/>
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.cpp"/>
|
||||
<File RelativePath="..\..\Source\model\Drawable\jucer_DrawableTypeHandler.h"/>
|
||||
</Filter>
|
||||
<Filter Name="Project">
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@
|
|||
file="Source/model/Drawable/jucer_DrawableDocument.cpp"/>
|
||||
<FILE id="qMcrWuCal" name="jucer_DrawableDocument.h" compile="0" resource="0"
|
||||
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"
|
||||
resource="0" file="Source/model/Drawable/jucer_DrawableTypeHandler.h"/>
|
||||
</GROUP>
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ namespace Ids
|
|||
DECLARE_ID (resource);
|
||||
DECLARE_ID (className);
|
||||
DECLARE_ID (classDesc);
|
||||
DECLARE_ID (controlPoint);
|
||||
const Identifier class_ ("class");
|
||||
const Identifier id_ ("id");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,80 +27,6 @@
|
|||
#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
|
||||
{
|
||||
|
|
@ -158,12 +84,6 @@ void DrawableDocument::checkRootObject()
|
|||
if (markersY == 0)
|
||||
markersY = new MarkerList (*this, false);
|
||||
|
||||
/* if ((int) getCanvasWidth().getValue() <= 0)
|
||||
getCanvasWidth() = 500;
|
||||
|
||||
if ((int) getCanvasHeight().getValue() <= 0)
|
||||
getCanvasHeight() = 500;
|
||||
*/
|
||||
DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode());
|
||||
recursivelyUpdateIDs (rootObject);
|
||||
}
|
||||
|
|
@ -325,35 +245,30 @@ const int menuItemOffset = 0x63451fa4;
|
|||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
|
@ -391,23 +306,11 @@ const RelativeCoordinate DrawableDocument::findNamedCoordinate (const String& ob
|
|||
{
|
||||
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())
|
||||
{
|
||||
/* 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 (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));
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
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:
|
||||
DrawableTypeInstance (DrawableDocument& document_, const ValueTree& state_);
|
||||
|
|
@ -43,8 +56,11 @@ public:
|
|||
|
||||
Value getValue (const Identifier& name) const;
|
||||
void createProperties (Array <PropertyComponent*>& props);
|
||||
const Rectangle<float> getBounds();
|
||||
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;
|
||||
|
|
@ -61,31 +77,26 @@ private:
|
|||
class DrawableTypeHandler
|
||||
{
|
||||
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 const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition) = 0;
|
||||
virtual void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props) = 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 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())
|
||||
return false;
|
||||
return;
|
||||
|
||||
newBounds.setSize (jmax (1.0f, newBounds.getWidth()),
|
||||
jmax (1.0f, newBounds.getHeight()));
|
||||
|
|
@ -101,20 +112,28 @@ protected:
|
|||
if (xScale == 1.0 && yScale == 1.0
|
||||
&& std::abs (newBounds.getX() - oldBounds.getX()) < tolerance
|
||||
&& std::abs (newBounds.getY() - oldBounds.getY()) < tolerance)
|
||||
return false;
|
||||
return;
|
||||
|
||||
const double xOffset = newBounds.getX() - xScale * oldBounds.getX();
|
||||
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);
|
||||
|
||||
cp->setPosition (point, undoManager);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -124,200 +143,28 @@ private:
|
|||
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:
|
||||
DrawableImageHandler() : DrawableTypeHandler ("Image", DrawableImage::valueTreeType, true) {}
|
||||
~DrawableImageHandler() {}
|
||||
DrawableTypeManager();
|
||||
~DrawableTypeManager();
|
||||
|
||||
const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition)
|
||||
{
|
||||
Image tempImage (Image::ARGB, 100, 100, true);
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (DrawableTypeManager);
|
||||
|
||||
{
|
||||
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);
|
||||
}
|
||||
//==============================================================================
|
||||
int getNumHandlers() const { return handlers.size(); }
|
||||
DrawableTypeHandler* getHandler (const int index) const { return handlers [index]; }
|
||||
|
||||
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);
|
||||
}
|
||||
DrawableTypeHandler* getHandlerFor (const Identifier& type);
|
||||
|
||||
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props)
|
||||
{
|
||||
}
|
||||
const StringArray getNewItemList();
|
||||
const ValueTree createNewItem (const int index, DrawableDocument& document, const Point<float>& approxPosition);
|
||||
|
||||
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item)
|
||||
{
|
||||
}
|
||||
|
||||
void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds)
|
||||
{
|
||||
DrawableImage::ValueTreeWrapper wrapper (item.getState());
|
||||
|
||||
RelativePoint points[3] = { wrapper.getTargetPositionForTopLeft(),
|
||||
wrapper.getTargetPositionForTopRight(),
|
||||
wrapper.getTargetPositionForBottomLeft() };
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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__
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ public:
|
|||
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()
|
||||
{
|
||||
getUndoManager().beginNewTransaction();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ class DrawableEditorCanvas : public EditorCanvasBase,
|
|||
public Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
DrawableEditorCanvas (DrawableEditor& editor_)
|
||||
: editor (editor_)
|
||||
{
|
||||
|
|
@ -48,6 +49,11 @@ public:
|
|||
shutdown();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
UndoManager& getUndoManager() throw() { return *getDocument().getUndoManager(); }
|
||||
DrawableEditor& getEditor() throw() { return editor; }
|
||||
DrawableDocument& getDocument() throw() { return editor.getDocument(); }
|
||||
|
||||
Component* createComponentHolder()
|
||||
{
|
||||
return new DrawableComponent (this);
|
||||
|
|
@ -67,7 +73,7 @@ public:
|
|||
else
|
||||
{
|
||||
const Rectangle<float> damage (drawable->refreshFromValueTree (doc.getRootDrawableNode().getState(), &doc));
|
||||
getComponentHolder()->repaint (damage.getSmallestIntegerContainer());
|
||||
getComponentHolder()->repaint (objectSpaceToScreenSpace (damage.getSmallestIntegerContainer()));
|
||||
}
|
||||
|
||||
startTimer (500);
|
||||
|
|
@ -81,14 +87,10 @@ public:
|
|||
void setCanvasBounds (const Rectangle<int>& newBounds) {}
|
||||
bool canResizeCanvas() const { return false; }
|
||||
|
||||
MarkerListBase& getMarkerList (bool isX)
|
||||
//==============================================================================
|
||||
const ValueTree getObjectState (const String& objectId)
|
||||
{
|
||||
return getDocument().getMarkerList (isX);
|
||||
}
|
||||
|
||||
double limitMarkerPosition (double pos)
|
||||
{
|
||||
return pos;
|
||||
return getDocument().findDrawableState (objectId, false);
|
||||
}
|
||||
|
||||
const SelectedItems::ItemType findObjectIdAt (const Point<int>& position)
|
||||
|
|
@ -130,30 +132,36 @@ public:
|
|||
|
||||
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; }
|
||||
|
||||
const ValueTree getObjectState (const String& objectId)
|
||||
{
|
||||
return getDocument().findDrawableState (objectId, false);
|
||||
}
|
||||
|
||||
void getObjectPositionDependencies (const ValueTree& state, Array<ValueTree>& deps)
|
||||
{
|
||||
DrawableDocument& doc = getDocument();
|
||||
DrawableTypeInstance item (doc, state);
|
||||
|
||||
Array <RelativePoint> points;
|
||||
OwnedArray <ControlPoint> points;
|
||||
item.getAllControlPoints (points);
|
||||
|
||||
StringArray anchors;
|
||||
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)
|
||||
|
|
@ -207,12 +215,15 @@ public:
|
|||
return RelativeRectangle();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ControlPointComponent : public OverlayItemComponent
|
||||
{
|
||||
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()
|
||||
|
|
@ -221,29 +232,79 @@ public:
|
|||
|
||||
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)
|
||||
{
|
||||
isDragging = false;
|
||||
|
||||
if (e.mods.isPopupMenu())
|
||||
{
|
||||
canvas->showPopupMenu (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mouseDownResult = canvas->getSelection().addToSelectionOnMouseDown (selectionId, e.mods);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (isDragging)
|
||||
{
|
||||
canvas->endDrag (e.getEventRelativeTo (getParentComponent()));
|
||||
}
|
||||
}
|
||||
|
||||
void updatePosition (const RelativePoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder)
|
||||
void mouseDoubleClick (const MouseEvent& e)
|
||||
{
|
||||
const Point<float> p (point.resolve (nameFinder));
|
||||
setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 5, 5));
|
||||
}
|
||||
|
||||
void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
@ -251,11 +312,11 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
DrawableTypeInstance item (getDocument(), state);
|
||||
Array<RelativePoint> points;
|
||||
DrawableTypeInstance item (getDocument(), controlPointEditingTarget);
|
||||
OwnedArray <ControlPoint> points;
|
||||
item.getAllControlPoints (points);
|
||||
|
||||
Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (state).getID());
|
||||
Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID());
|
||||
DrawableComposite* parentDrawable = d->getParent();
|
||||
|
||||
comps.removeRange (points.size(), comps.size());
|
||||
|
|
@ -269,15 +330,27 @@ public:
|
|||
|
||||
if (c == 0)
|
||||
{
|
||||
c = new ControlPointComponent (this);
|
||||
c = new ControlPointComponent (this, controlPointEditingTarget, i);
|
||||
comps.set (i, 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()
|
||||
{
|
||||
return editor.getSelection();
|
||||
|
|
@ -303,25 +376,32 @@ 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:
|
||||
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()
|
||||
{
|
||||
getUndoManager().beginNewTransaction();
|
||||
}
|
||||
~ObjectDragOperation() {}
|
||||
|
||||
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 getSnapPointsY (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); }
|
||||
|
|
@ -330,56 +410,144 @@ public:
|
|||
|
||||
void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps)
|
||||
{
|
||||
static_cast <DrawableEditorCanvas*> (canvas)->getObjectPositionDependencies (state, deps);
|
||||
drawableCanvas->getObjectPositionDependencies (state, deps);
|
||||
}
|
||||
|
||||
const Rectangle<float> getObjectPosition (const ValueTree& state)
|
||||
{
|
||||
return static_cast <DrawableEditorCanvas*> (canvas)->getObjectPositionFloat (state);
|
||||
return drawableCanvas->getObjectPositionFloat (state);
|
||||
}
|
||||
|
||||
void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds)
|
||||
{
|
||||
static_cast <DrawableEditorCanvas*> (canvas)->setObjectPositionFloat (state, 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:
|
||||
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)
|
||||
{
|
||||
drawableCanvas->getObjectPositionDependencies (drawableItem.getState(), deps);
|
||||
}
|
||||
|
||||
const Rectangle<float> getObjectPosition (const ValueTree& 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
DrawableEditorCanvas* drawableCanvas;
|
||||
DrawableTypeInstance drawableItem;
|
||||
Drawable* drawable;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent,
|
||||
const ResizableBorderComponent::Zone& zone)
|
||||
{
|
||||
DragOperation* d = new DragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone);
|
||||
|
||||
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);
|
||||
|
||||
DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode());
|
||||
|
||||
for (int i = mainGroup.getNumDrawables(); --i >= 0;)
|
||||
{
|
||||
const ValueTree v (mainGroup.getDrawableState (i));
|
||||
|
||||
if (editor.getSelection().isSelected (v[Drawable::ValueTreeWrapperBase::idProperty]))
|
||||
selected.add (v);
|
||||
else
|
||||
unselected.add (v);
|
||||
}
|
||||
}
|
||||
|
||||
d->initialise (selected, unselected);
|
||||
return d;
|
||||
drag->initialise (selected, unselected);
|
||||
return drag;
|
||||
}
|
||||
|
||||
UndoManager& getUndoManager()
|
||||
{
|
||||
return *getDocument().getUndoManager();
|
||||
}
|
||||
|
||||
DrawableEditor& getEditor() throw() { return editor; }
|
||||
DrawableDocument& getDocument() throw() { return editor.getDocument(); }
|
||||
|
||||
void timerCallback()
|
||||
{
|
||||
stopTimer();
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public:
|
|||
else
|
||||
{
|
||||
isDragging = true;
|
||||
canvas->beginDrag (e.getEventRelativeTo (getParentComponent()), dragZone);
|
||||
canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), dragZone);
|
||||
canvas->showSizeGuides();
|
||||
}
|
||||
}
|
||||
|
|
@ -121,7 +121,6 @@ public:
|
|||
sizeGuides.getUnchecked(i)->updatePosition (bounds);
|
||||
}
|
||||
|
||||
canvas->updateExtraComponentsForObject (objectState, getParentComponent(), extraEditorComps);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +201,6 @@ private:
|
|||
const int borderThickness;
|
||||
OwnedArray <SizeGuideComponent> sizeGuides;
|
||||
bool isDragging;
|
||||
OwnedArray <OverlayItemComponent> extraEditorComps;
|
||||
|
||||
const Rectangle<int> getCentreArea() const
|
||||
{
|
||||
|
|
@ -404,6 +402,7 @@ public:
|
|||
resizers.clear();
|
||||
markersX.clear();
|
||||
markersY.clear();
|
||||
controlPoints.clear();
|
||||
deleteAllChildren();
|
||||
}
|
||||
|
||||
|
|
@ -420,7 +419,10 @@ public:
|
|||
if (e.mods.isPopupMenu())
|
||||
{
|
||||
if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse))
|
||||
{
|
||||
canvas->enableResizingMode();
|
||||
getSelection().selectOnly (underMouse);
|
||||
}
|
||||
|
||||
canvas->showPopupMenu (underMouse.isNotEmpty());
|
||||
}
|
||||
|
|
@ -436,6 +438,7 @@ public:
|
|||
{
|
||||
mouseDownCompUID = underMouse;
|
||||
canvas->deselectNonDraggableObjects();
|
||||
canvas->enableResizingMode();
|
||||
mouseDownResult = getSelection().addToSelectionOnMouseDown (mouseDownCompUID, e.mods);
|
||||
|
||||
updateResizeFrames();
|
||||
|
|
@ -456,8 +459,9 @@ public:
|
|||
if (! isDraggingClickedComp)
|
||||
{
|
||||
isDraggingClickedComp = true;
|
||||
canvas->enableResizingMode();
|
||||
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);
|
||||
|
|
@ -526,15 +530,9 @@ public:
|
|||
SelectedItems& getSelection() { return canvas->getSelection(); }
|
||||
SelectedItems& getLassoSelection() { return getSelection(); }
|
||||
|
||||
void resized()
|
||||
{
|
||||
updateMarkers();
|
||||
updateResizeFrames();
|
||||
}
|
||||
|
||||
void changeListenerCallback (void*)
|
||||
{
|
||||
updateResizeFrames();
|
||||
update();
|
||||
}
|
||||
|
||||
void modifierKeysChanged (const ModifierKeys&)
|
||||
|
|
@ -571,6 +569,7 @@ public:
|
|||
void update()
|
||||
{
|
||||
updateResizeFrames();
|
||||
updateControlPoints();
|
||||
updateMarkers();
|
||||
}
|
||||
|
||||
|
|
@ -582,9 +581,16 @@ private:
|
|||
SelectedItems::ItemType mouseDownCompUID;
|
||||
OwnedArray <ResizeFrame> resizers;
|
||||
OwnedArray <MarkerComponent> markersX, markersY;
|
||||
OwnedArray <OverlayItemComponent> controlPoints;
|
||||
|
||||
void updateResizeFrames()
|
||||
{
|
||||
if (! canvas->isResizingMode())
|
||||
{
|
||||
resizers.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedItems& selection = getSelection();
|
||||
StringArray requiredIds;
|
||||
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)
|
||||
{
|
||||
MarkerListBase& markerList = canvas->getMarkerList (isX);
|
||||
|
|
@ -827,6 +844,21 @@ const Rectangle<int> EditorCanvasBase::objectSpaceToScreenSpace (const Rectangle
|
|||
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)
|
||||
{
|
||||
|
|
@ -938,6 +970,8 @@ void EditorCanvasBase::endDrag (const MouseEvent& e)
|
|||
{
|
||||
dragger->drag (e, e.getPosition() - origin);
|
||||
dragger = 0;
|
||||
|
||||
getUndoManager().beginNewTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ public:
|
|||
virtual void deselectNonDraggableObjects() = 0;
|
||||
virtual void findLassoItemsInArea (Array <SelectedItems::ItemType>& itemsFound, const Rectangle<int>& area) = 0;
|
||||
|
||||
//==============================================================================
|
||||
class DragOperation
|
||||
{
|
||||
public:
|
||||
|
|
@ -105,6 +106,12 @@ public:
|
|||
void continueDrag (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; }
|
||||
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:
|
||||
//==============================================================================
|
||||
const BorderSize border;
|
||||
Point<int> origin;
|
||||
double scaleFactor;
|
||||
ValueTree controlPointEditingTarget;
|
||||
|
||||
friend class OverlayItemComponent;
|
||||
class ResizeFrame;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ MainWindow::MainWindow()
|
|||
// don't want the window to take focus when the title-bar is clicked..
|
||||
setWantsKeyboardFocus (false);
|
||||
|
||||
//getPeer()->setCurrentRenderingEngine (0);
|
||||
getPeer()->setCurrentRenderingEngine (0);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#ifndef __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__
|
||||
#define __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__
|
||||
|
||||
class FillTypeEditorComponent;
|
||||
|
||||
//==============================================================================
|
||||
class PopupFillSelector : public Component,
|
||||
public ChangeListener,
|
||||
|
|
@ -33,8 +35,10 @@ class PopupFillSelector : public Component,
|
|||
public ButtonListener
|
||||
{
|
||||
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_)
|
||||
{
|
||||
colourButton.setButtonText ("Colour");
|
||||
|
|
@ -76,15 +80,6 @@ public:
|
|||
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()
|
||||
{
|
||||
const int y = 2, w = 80, h = 22;
|
||||
|
|
@ -99,18 +94,44 @@ public:
|
|||
|
||||
void buttonClicked (Button* b)
|
||||
{
|
||||
RelativePoint gp1, gp2;
|
||||
FillType currentFill (readFillType (&gp1, &gp2));
|
||||
|
||||
if (b == &colourButton)
|
||||
{
|
||||
setFillType (colourPicker.getCurrentColour());
|
||||
if (! currentFill.isColour())
|
||||
setFillType (colourPicker.getCurrentColour());
|
||||
}
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
RelativePoint gp1, gp2;
|
||||
const FillType currentFill (readFillType (&gp1, &gp2));
|
||||
FillType currentFill (readFillType (&gp1, &gp2));
|
||||
|
||||
if (currentFill != newFill)
|
||||
{
|
||||
|
|
@ -146,7 +167,7 @@ public:
|
|||
|
||||
void refresh()
|
||||
{
|
||||
const FillType newFill (readFillType (0, 0));
|
||||
FillType newFill (readFillType (0, 0));
|
||||
|
||||
colourPicker.setVisible (newFill.isColour());
|
||||
gradientPicker.setVisible (newFill.isGradient());
|
||||
|
|
@ -158,6 +179,12 @@ public:
|
|||
}
|
||||
else if (newFill.isGradient())
|
||||
{
|
||||
if (newFill.gradient->getNumColours() <= 1)
|
||||
{
|
||||
newFill = FillType (defaultGradient);
|
||||
Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager);
|
||||
}
|
||||
|
||||
gradientButton.setToggleState (true, false);
|
||||
gradientPicker.setGradient (*newFill.gradient);
|
||||
}
|
||||
|
|
@ -178,8 +205,8 @@ private:
|
|||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
GradientDesigner()
|
||||
: gradient (Colours::red, 0.0f, 0.0f, Colours::blue, 200.0f, 200.0f, false),
|
||||
GradientDesigner (const ColourGradient& gradient_)
|
||||
: gradient (gradient_),
|
||||
selectedPoint (-1),
|
||||
dragging (false),
|
||||
draggingNewPoint (false),
|
||||
|
|
@ -302,8 +329,10 @@ private:
|
|||
|
||||
void setGradient (const ColourGradient& newGradient)
|
||||
{
|
||||
if (newGradient != gradient)
|
||||
if (newGradient != gradient || selectedPoint < 0)
|
||||
{
|
||||
jassert (newGradient.getNumColours() > 1);
|
||||
|
||||
gradient = newGradient;
|
||||
|
||||
if (selectedPoint < 0)
|
||||
|
|
@ -375,8 +404,10 @@ private:
|
|||
};
|
||||
|
||||
//==============================================================================
|
||||
FillTypeEditorComponent* owner;
|
||||
StoredSettings::ColourSelectorWithSwatches colourPicker;
|
||||
GradientDesigner gradientPicker;
|
||||
ColourGradient defaultGradient;
|
||||
ValueTree fillState;
|
||||
UndoManager* undoManager;
|
||||
|
||||
|
|
@ -404,6 +435,8 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
const ColourGradient getDefaultGradient() const;
|
||||
|
||||
void paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::grey);
|
||||
|
|
@ -447,7 +480,12 @@ public:
|
|||
void mouseDown (const MouseEvent& e)
|
||||
{
|
||||
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(); }
|
||||
|
|
@ -462,6 +500,7 @@ private:
|
|||
FillType fillType;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FillTypePropertyComponent : public PropertyComponent
|
||||
{
|
||||
|
|
@ -484,6 +523,8 @@ public:
|
|||
editor.setBounds (getLookAndFeel().getPropertyComponentContentPosition (*this));
|
||||
}
|
||||
|
||||
virtual const ColourGradient getDefaultGradient() = 0;
|
||||
|
||||
void refresh() {}
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
#include "../jucer_Headers.h"
|
||||
#include "jucer_FillTypePropertyComponent.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -406,3 +407,11 @@ RelativeRectangleLayoutManager::ComponentPosition::ComponentPosition (Component*
|
|||
: 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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
#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)
|
||||
.getDistanceFrom (point2.transformedBy (transform)));
|
||||
lookupTable.malloc (numEntries);
|
||||
|
|
@ -83946,10 +83963,6 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType
|
|||
{
|
||||
v.setProperty (type, "solid", 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())
|
||||
{
|
||||
|
|
@ -83964,19 +83977,12 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType
|
|||
<< " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB());
|
||||
|
||||
v.setProperty (colours, s.trimStart(), undoManager);
|
||||
v.removeProperty (colour, undoManager);
|
||||
}
|
||||
else if (fillType.isTiledImage())
|
||||
{
|
||||
v.setProperty (type, "image", undoManager);
|
||||
|
||||
jassertfalse; //xxx todo
|
||||
|
||||
v.removeProperty (gradientPoint1, undoManager);
|
||||
v.removeProperty (gradientPoint2, undoManager);
|
||||
v.removeProperty (radial, undoManager);
|
||||
v.removeProperty (colours, undoManager);
|
||||
v.removeProperty (colour, undoManager);
|
||||
}
|
||||
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 of inlined file: juce_Drawable.cpp ***/
|
||||
|
||||
|
|
@ -84325,12 +84316,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const
|
|||
|
||||
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
|
||||
|
|
@ -84431,12 +84417,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
|
|||
|
||||
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
|
||||
|
|
@ -84919,6 +84900,8 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre
|
|||
|| controlPoints[1] != newControlPoint[1]
|
||||
|| controlPoints[2] != newControlPoint[2])
|
||||
{
|
||||
const Rectangle<float> damage (getBounds());
|
||||
|
||||
opacity = newOpacity;
|
||||
overlayColour = newOverlayColour;
|
||||
controlPoints[0] = newControlPoint[0];
|
||||
|
|
@ -84934,7 +84917,7 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre
|
|||
image = newImage;
|
||||
}
|
||||
|
||||
return getBounds();
|
||||
return damage.getUnion (getBounds());
|
||||
}
|
||||
|
||||
ImageCache::release (newImage);
|
||||
|
|
@ -85118,12 +85101,16 @@ Drawable* DrawablePath::createCopy() const
|
|||
|
||||
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::capStyle ("capStyle");
|
||||
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_)
|
||||
: ValueTreeWrapperBase (state_)
|
||||
|
|
@ -85131,6 +85118,11 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
|||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::getPathState()
|
||||
{
|
||||
return state.getOrCreateChildWithName (path, 0);
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::getMainFillState()
|
||||
{
|
||||
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,
|
||||
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
|
||||
|
|
@ -85170,7 +85163,8 @@ const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate
|
|||
void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1,
|
||||
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
|
||||
|
|
@ -85196,15 +85190,50 @@ void DrawablePath::ValueTreeWrapper::setStrokeType (const PathStrokeType& newStr
|
|||
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::getPath (RelativePointPath& p) const
|
||||
bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const
|
||||
{
|
||||
RelativePointPath newPath (state [path]);
|
||||
p.swapWith (newPath);
|
||||
return state [nonZeroWinding];
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager)
|
||||
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (path, newPath, 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()
|
||||
{
|
||||
}
|
||||
|
||||
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw()
|
||||
{
|
||||
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*)
|
||||
|
|
@ -85232,8 +85261,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree
|
|||
|
||||
const PathStrokeType newStroke (v.getStrokeType());
|
||||
|
||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath());
|
||||
v.getPath (*newRelativePath);
|
||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree));
|
||||
|
||||
Path newPath;
|
||||
newRelativePath->createPath (newPath, parent);
|
||||
|
|
@ -85268,9 +85296,14 @@ const ValueTree DrawablePath::createValueTree (ImageProvider*) const
|
|||
v.setStrokeType (strokeType, 0);
|
||||
|
||||
if (relativePath != 0)
|
||||
v.setPath (relativePath->toString(), 0);
|
||||
{
|
||||
relativePath->writeTo (tree, 0);
|
||||
}
|
||||
else
|
||||
v.setPath (path.toString(), 0);
|
||||
{
|
||||
RelativePointPath rp (path);
|
||||
rp.writeTo (tree, 0);
|
||||
}
|
||||
|
||||
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_)
|
||||
: x (x_), y (y_)
|
||||
{
|
||||
|
|
@ -92911,88 +92949,83 @@ RelativePointPath::RelativePointPath (const RelativePointPath& other)
|
|||
: usesNonZeroWinding (true),
|
||||
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),
|
||||
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];
|
||||
|
||||
if (firstChar == 0)
|
||||
break;
|
||||
|
||||
const juce_wchar secondChar = s[i + 1];
|
||||
|
||||
if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar))
|
||||
switch (i.elementType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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 == '#')
|
||||
++i;
|
||||
void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager)
|
||||
{
|
||||
DrawablePath::ValueTreeWrapper wrapper (state);
|
||||
wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager);
|
||||
|
||||
for (int j = 0; j < numValues; ++j)
|
||||
ValueTree pathTree (wrapper.getPathState());
|
||||
pathTree.removeAllChildren (undoManager);
|
||||
|
||||
for (int i = 0; i < elements.size(); ++i)
|
||||
pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_)
|
||||
{
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -93129,16 +93130,12 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint,
|
|||
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
|
||||
|
|
@ -93162,16 +93159,13 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R
|
|||
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
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 7
|
||||
#define JUCE_BUILDNUMBER 8
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -13094,12 +13094,22 @@ public:
|
|||
*/
|
||||
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
|
||||
whether a node is valid).
|
||||
@see getOrCreateChildWithName
|
||||
*/
|
||||
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.
|
||||
|
||||
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;
|
||||
int indexOf (const ValueTree& child) const;
|
||||
ValueTree getChildWithName (const Identifier& type) const;
|
||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager);
|
||||
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const;
|
||||
void addChild (SharedObject* child, int index, UndoManager*);
|
||||
void removeChild (int childIndex, UndoManager*);
|
||||
|
|
@ -42124,6 +42135,9 @@ public:
|
|||
/** Creates an absolute point, relative to the origin. */
|
||||
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. */
|
||||
RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y);
|
||||
|
||||
|
|
@ -42246,7 +42260,8 @@ public:
|
|||
|
||||
RelativePointPath();
|
||||
RelativePointPath (const RelativePointPath& other);
|
||||
RelativePointPath (const String& stringVersion);
|
||||
RelativePointPath (const ValueTree& drawable);
|
||||
RelativePointPath (const Path& path);
|
||||
~RelativePointPath();
|
||||
|
||||
/** 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. */
|
||||
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. */
|
||||
void swapWith (RelativePointPath& other) throw();
|
||||
|
|
@ -42284,7 +42296,7 @@ public:
|
|||
public:
|
||||
ElementBase (ElementType type);
|
||||
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 RelativePoint* getControlPoints (int& numPoints) = 0;
|
||||
|
||||
|
|
@ -42300,7 +42312,7 @@ public:
|
|||
public:
|
||||
StartSubPath (const RelativePoint& pos);
|
||||
~StartSubPath() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -42316,7 +42328,7 @@ public:
|
|||
public:
|
||||
CloseSubPath();
|
||||
~CloseSubPath() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -42330,7 +42342,7 @@ public:
|
|||
public:
|
||||
LineTo (const RelativePoint& endPoint);
|
||||
~LineTo() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -42346,7 +42358,7 @@ public:
|
|||
public:
|
||||
QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint);
|
||||
~QuadraticTo() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -42362,7 +42374,7 @@ public:
|
|||
public:
|
||||
CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint);
|
||||
~CubicTo() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -42379,7 +42391,7 @@ public:
|
|||
private:
|
||||
bool containsDynamicPoints;
|
||||
|
||||
void parseString (const String& s);
|
||||
void parse (const ValueTree& state);
|
||||
|
||||
RelativePointPath& operator= (const RelativePointPath&);
|
||||
};
|
||||
|
|
@ -42601,13 +42613,8 @@ public:
|
|||
const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2,
|
||||
UndoManager* undoManager);
|
||||
|
||||
protected:
|
||||
ValueTree state;
|
||||
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
|
||||
|
|
@ -58599,7 +58606,7 @@ public:
|
|||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @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. */
|
||||
class ValueTreeWrapper : public ValueTreeWrapperBase
|
||||
|
|
@ -58620,10 +58627,31 @@ public:
|
|||
const PathStrokeType getStrokeType() const;
|
||||
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;
|
||||
};
|
||||
|
||||
juce_UseDebuggingNewOperator
|
||||
|
|
|
|||
|
|
@ -381,6 +381,18 @@ ValueTree ValueTree::SharedObject::getChildWithName (const Identifier& typeToMat
|
|||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return object != 0 ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid;
|
||||
|
|
|
|||
|
|
@ -214,12 +214,22 @@ public:
|
|||
*/
|
||||
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
|
||||
whether a node is valid).
|
||||
@see getOrCreateChildWithName
|
||||
*/
|
||||
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.
|
||||
|
||||
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;
|
||||
int indexOf (const ValueTree& child) const;
|
||||
ValueTree getChildWithName (const Identifier& type) const;
|
||||
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager);
|
||||
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const;
|
||||
void addChild (SharedObject* child, int index, UndoManager*);
|
||||
void removeChild (int childIndex, UndoManager*);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 7
|
||||
#define JUCE_BUILDNUMBER 8
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlo
|
|||
jassert (point1.getX() != 987654.0f);
|
||||
#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)
|
||||
.getDistanceFrom (point2.transformedBy (transform)));
|
||||
lookupTable.malloc (numEntries);
|
||||
|
|
|
|||
|
|
@ -238,10 +238,6 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType
|
|||
{
|
||||
v.setProperty (type, "solid", 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())
|
||||
{
|
||||
|
|
@ -256,19 +252,12 @@ void Drawable::ValueTreeWrapperBase::writeFillType (ValueTree& v, const FillType
|
|||
<< " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB());
|
||||
|
||||
v.setProperty (colours, s.trimStart(), undoManager);
|
||||
v.removeProperty (colour, undoManager);
|
||||
}
|
||||
else if (fillType.isTiledImage())
|
||||
{
|
||||
v.setProperty (type, "image", undoManager);
|
||||
|
||||
jassertfalse; //xxx todo
|
||||
|
||||
v.removeProperty (gradientPoint1, undoManager);
|
||||
v.removeProperty (gradientPoint2, undoManager);
|
||||
v.removeProperty (radial, undoManager);
|
||||
v.removeProperty (colours, undoManager);
|
||||
v.removeProperty (colour, undoManager);
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -255,13 +255,8 @@ public:
|
|||
const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2,
|
||||
UndoManager* undoManager);
|
||||
|
||||
protected:
|
||||
ValueTree state;
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
|
@ -466,12 +461,7 @@ ValueTree DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -313,6 +313,8 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre
|
|||
|| controlPoints[1] != newControlPoint[1]
|
||||
|| controlPoints[2] != newControlPoint[2])
|
||||
{
|
||||
const Rectangle<float> damage (getBounds());
|
||||
|
||||
opacity = newOpacity;
|
||||
overlayColour = newOverlayColour;
|
||||
controlPoints[0] = newControlPoint[0];
|
||||
|
|
@ -328,7 +330,7 @@ const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tre
|
|||
image = newImage;
|
||||
}
|
||||
|
||||
return getBounds();
|
||||
return damage.getUnion (getBounds());
|
||||
}
|
||||
|
||||
ImageCache::release (newImage);
|
||||
|
|
|
|||
|
|
@ -183,12 +183,16 @@ Drawable* DrawablePath::createCopy() const
|
|||
//==============================================================================
|
||||
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::capStyle ("capStyle");
|
||||
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_)
|
||||
|
|
@ -197,6 +201,11 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
|
|||
jassert (state.hasType (valueTreeType));
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::getPathState()
|
||||
{
|
||||
return state.getOrCreateChildWithName (path, 0);
|
||||
}
|
||||
|
||||
ValueTree DrawablePath::ValueTreeWrapper::getMainFillState()
|
||||
{
|
||||
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,
|
||||
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
|
||||
|
|
@ -236,7 +246,8 @@ const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate
|
|||
void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1,
|
||||
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
|
||||
|
|
@ -262,17 +273,54 @@ void DrawablePath::ValueTreeWrapper::setStrokeType (const PathStrokeType& newStr
|
|||
? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager);
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::getPath (RelativePointPath& p) const
|
||||
bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const
|
||||
{
|
||||
RelativePointPath newPath (state [path]);
|
||||
p.swapWith (newPath);
|
||||
return state [nonZeroWinding];
|
||||
}
|
||||
|
||||
void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager)
|
||||
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager)
|
||||
{
|
||||
state.setProperty (path, newPath, 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()
|
||||
{
|
||||
}
|
||||
|
||||
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw()
|
||||
{
|
||||
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*)
|
||||
{
|
||||
Rectangle<float> damageRect;
|
||||
|
|
@ -298,8 +346,7 @@ const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree
|
|||
|
||||
const PathStrokeType newStroke (v.getStrokeType());
|
||||
|
||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath());
|
||||
v.getPath (*newRelativePath);
|
||||
ScopedPointer<RelativePointPath> newRelativePath (new RelativePointPath (tree));
|
||||
|
||||
Path newPath;
|
||||
newRelativePath->createPath (newPath, parent);
|
||||
|
|
@ -334,9 +381,14 @@ const ValueTree DrawablePath::createValueTree (ImageProvider*) const
|
|||
v.setStrokeType (strokeType, 0);
|
||||
|
||||
if (relativePath != 0)
|
||||
v.setPath (relativePath->toString(), 0);
|
||||
{
|
||||
relativePath->writeTo (tree, 0);
|
||||
}
|
||||
else
|
||||
v.setPath (path.toString(), 0);
|
||||
{
|
||||
RelativePointPath rp (path);
|
||||
rp.writeTo (tree, 0);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public:
|
|||
/** @internal */
|
||||
static const Identifier valueTreeType;
|
||||
/** @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. */
|
||||
|
|
@ -141,10 +141,31 @@ public:
|
|||
const PathStrokeType getStrokeType() const;
|
||||
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
|
||||
|
||||
#include "juce_RelativeCoordinate.h"
|
||||
#include "../drawables/juce_DrawablePath.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_)
|
||||
: x (x_), y (y_)
|
||||
{
|
||||
|
|
@ -625,88 +631,83 @@ RelativePointPath::RelativePointPath (const RelativePointPath& other)
|
|||
: usesNonZeroWinding (true),
|
||||
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),
|
||||
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];
|
||||
|
||||
if (firstChar == 0)
|
||||
break;
|
||||
|
||||
const juce_wchar secondChar = s[i + 1];
|
||||
|
||||
if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar))
|
||||
switch (i.elementType)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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 == '#')
|
||||
++i;
|
||||
void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager)
|
||||
{
|
||||
DrawablePath::ValueTreeWrapper wrapper (state);
|
||||
wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager);
|
||||
|
||||
for (int j = 0; j < numValues; ++j)
|
||||
ValueTree pathTree (wrapper.getPathState());
|
||||
pathTree.removeAllChildren (undoManager);
|
||||
|
||||
for (int i = 0; i < elements.size(); ++i)
|
||||
pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_)
|
||||
{
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -848,16 +817,12 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint,
|
|||
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
|
||||
|
|
@ -882,16 +847,13 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R
|
|||
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
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "juce_Path.h"
|
||||
#include "juce_Rectangle.h"
|
||||
#include "../../../containers/juce_OwnedArray.h"
|
||||
#include "../../../containers/juce_ValueTree.h"
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -289,6 +290,9 @@ public:
|
|||
/** Creates an absolute point, relative to the origin. */
|
||||
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. */
|
||||
RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y);
|
||||
|
||||
|
|
@ -416,7 +420,8 @@ public:
|
|||
//==============================================================================
|
||||
RelativePointPath();
|
||||
RelativePointPath (const RelativePointPath& other);
|
||||
RelativePointPath (const String& stringVersion);
|
||||
RelativePointPath (const ValueTree& drawable);
|
||||
RelativePointPath (const Path& path);
|
||||
~RelativePointPath();
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -426,11 +431,8 @@ public:
|
|||
/** Returns true if the path contains any non-fixed points. */
|
||||
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. */
|
||||
void swapWith (RelativePointPath& other) throw();
|
||||
|
|
@ -457,7 +459,7 @@ public:
|
|||
public:
|
||||
ElementBase (ElementType type);
|
||||
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 RelativePoint* getControlPoints (int& numPoints) = 0;
|
||||
|
||||
|
|
@ -473,7 +475,7 @@ public:
|
|||
public:
|
||||
StartSubPath (const RelativePoint& pos);
|
||||
~StartSubPath() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -489,7 +491,7 @@ public:
|
|||
public:
|
||||
CloseSubPath();
|
||||
~CloseSubPath() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -503,7 +505,7 @@ public:
|
|||
public:
|
||||
LineTo (const RelativePoint& endPoint);
|
||||
~LineTo() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -519,7 +521,7 @@ public:
|
|||
public:
|
||||
QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint);
|
||||
~QuadraticTo() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -535,7 +537,7 @@ public:
|
|||
public:
|
||||
CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint);
|
||||
~CubicTo() {}
|
||||
void write (OutputStream& out, ElementType lastTypeWritten) const;
|
||||
const ValueTree createTree() const;
|
||||
void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const;
|
||||
RelativePoint* getControlPoints (int& numPoints);
|
||||
|
||||
|
|
@ -553,7 +555,7 @@ public:
|
|||
private:
|
||||
bool containsDynamicPoints;
|
||||
|
||||
void parseString (const String& s);
|
||||
void parse (const ValueTree& state);
|
||||
|
||||
RelativePointPath& operator= (const RelativePointPath&);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ public:
|
|||
timeOutMs);
|
||||
|
||||
if (responseHeaders != 0)
|
||||
juce_getInternetFileHeaders (handle, *responseHeaders);
|
||||
juce_getInternetFileHeaders (handle, *responseHeaders);
|
||||
}
|
||||
|
||||
~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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue