Browse Source

More changes to Drawables + Jucer development.

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
0c541cfba2
32 changed files with 1863 additions and 1301 deletions
  1. +6
    -0
      extras/Jucer (experimental)/Builds/Linux/Makefile
  2. +4
    -0
      extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj
  3. +1
    -0
      extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj
  4. +1
    -0
      extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj
  5. +2
    -0
      extras/Jucer (experimental)/Jucer.jucer
  6. +1
    -0
      extras/Jucer (experimental)/Source/jucer_Headers.h
  7. +16
    -114
      extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp
  8. +491
    -0
      extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp
  9. +52
    -205
      extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h
  10. +1
    -2
      extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h
  11. +229
    -61
      extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h
  12. +45
    -11
      extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp
  13. +10
    -2
      extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h
  14. +1
    -1
      extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp
  15. +62
    -21
      extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h
  16. +9
    -0
      extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp
  17. +171
    -177
      juce_amalgamated.cpp
  18. +52
    -24
      juce_amalgamated.h
  19. +17
    -0
      src/containers/juce_ValueTree.cpp
  20. +12
    -1
      src/containers/juce_ValueTree.h
  21. +1
    -1
      src/core/juce_StandardHeader.h
  22. +1
    -1
      src/gui/graphics/colour/juce_ColourGradient.cpp
  23. +0
    -26
      src/gui/graphics/drawables/juce_Drawable.cpp
  24. +0
    -5
      src/gui/graphics/drawables/juce_Drawable.h
  25. +2
    -12
      src/gui/graphics/drawables/juce_DrawableComposite.cpp
  26. +3
    -1
      src/gui/graphics/drawables/juce_DrawableImage.cpp
  27. +66
    -14
      src/gui/graphics/drawables/juce_DrawablePath.cpp
  28. +25
    -4
      src/gui/graphics/drawables/juce_DrawablePath.h
  29. +85
    -123
      src/gui/graphics/geometry/juce_RelativeCoordinate.cpp
  30. +15
    -13
      src/gui/graphics/geometry/juce_RelativeCoordinate.h
  31. +1
    -1
      src/io/network/juce_URL.cpp
  32. +481
    -481
      src/native/linux/juce_linux_Network.cpp

+ 6
- 0
extras/Jucer (experimental)/Builds/Linux/Makefile View File

@@ -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"


+ 4
- 0
extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj View File

@@ -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,


+ 1
- 0
extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj View File

@@ -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">


+ 1
- 0
extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj View File

@@ -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">


+ 2
- 0
extras/Jucer (experimental)/Jucer.jucer View File

@@ -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>


+ 1
- 0
extras/Jucer (experimental)/Source/jucer_Headers.h View File

@@ -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");
}


+ 16
- 114
extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp View File

@@ -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));


+ 491
- 0
extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp View File

@@ -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);
}

+ 52
- 205
extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h View File

@@ -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);
return true;
cp->setPosition (point, undoManager);
}
}
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() {}
const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition)
{
Image tempImage (Image::ARGB, 100, 100, true);
{
Graphics g (tempImage);
g.fillAll (Colours::grey.withAlpha (0.3f));
g.setColour (Colours::red);
g.setFont (40.0f);
g.drawText ("?", 0, 0, 100, 100, Justification::centred, false);
}
DrawableTypeManager();
~DrawableTypeManager();
DrawableImage di;
di.setTransform (RelativePoint (approxPosition),
RelativePoint (approxPosition + Point<float> (100.0f, 0.0f)),
RelativePoint (approxPosition + Point<float> (0.0f, 100.0f)));
return di.createValueTree (&document);
}
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props)
{
}
juce_DeclareSingleton_SingleThreaded_Minimal (DrawableTypeManager);
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item)
{
}
void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds)
{
DrawableImage::ValueTreeWrapper wrapper (item.getState());
//==============================================================================
int getNumHandlers() const { return handlers.size(); }
DrawableTypeHandler* getHandler (const int index) const { return handlers [index]; }
RelativePoint points[3] = { wrapper.getTargetPositionForTopLeft(),
wrapper.getTargetPositionForTopRight(),
wrapper.getTargetPositionForBottomLeft() };
DrawableTypeHandler* getHandlerFor (const Identifier& type);
if (rescalePoints (points, 3, drawable->getBounds(), newBounds, drawable->getParent()))
{
UndoManager* undoManager = item.getDocument().getUndoManager();
wrapper.setTargetPositionForTopLeft (points[0], undoManager);
wrapper.setTargetPositionForTopRight (points[1], undoManager);
wrapper.setTargetPositionForBottomLeft (points[2], undoManager);
}
}
const StringArray getNewItemList();
const ValueTree createNewItem (const int index, DrawableDocument& document, const Point<float>& approxPosition);
void getAllControlPoints (DrawableTypeInstance& item, Array <RelativePoint>& points)
{
DrawableImage::ValueTreeWrapper wrapper (item.getState());
points.add (wrapper.getTargetPositionForTopLeft());
points.add (wrapper.getTargetPositionForTopRight());
points.add (wrapper.getTargetPositionForBottomLeft());
}
private:
OwnedArray <DrawableTypeHandler> handlers;
};
//==============================================================================
class DrawableCompositeHandler : public DrawableTypeHandler
{
public:
DrawableCompositeHandler() : DrawableTypeHandler ("Group", DrawableComposite::valueTreeType, false) {}
~DrawableCompositeHandler() {}
const ValueTree createNewInstance (DrawableDocument& document, const Point<float>& approxPosition)
{
return ValueTree::invalid;
}
void createPropertyEditors (DrawableTypeInstance& item, Array <PropertyComponent*>& props)
{
}
void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item)
{
}
void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle<float>& newBounds)
{
DrawableComposite::ValueTreeWrapper wrapper (item.getState());
RelativePoint points[3] = { wrapper.getTargetPositionForOrigin(),
wrapper.getTargetPositionForX1Y0(),
wrapper.getTargetPositionForX0Y1() };
if (rescalePoints (points, 3, drawable->getBounds(), newBounds, drawable->getParent()))
{
UndoManager* undoManager = item.getDocument().getUndoManager();
wrapper.setTargetPositionForOrigin (points[0], undoManager);
wrapper.setTargetPositionForX1Y0 (points[1], undoManager);
wrapper.setTargetPositionForX0Y1 (points[2], undoManager);
}
}
void getAllControlPoints (DrawableTypeInstance& item, Array <RelativePoint>& points)
{
DrawableComposite::ValueTreeWrapper wrapper (item.getState());
points.add (wrapper.getTargetPositionForOrigin());
points.add (wrapper.getTargetPositionForX1Y0());
points.add (wrapper.getTargetPositionForX0Y1());
}
};
#endif // __JUCER_DRAWABLETYPEHANDLER_H_7FB02E2F__

+ 1
- 2
extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h View File

@@ -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:


+ 229
- 61
extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h View File

@@ -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)
{
return getDocument().getMarkerList (isX);
}
double limitMarkerPosition (double pos)
//==============================================================================
const ValueTree getObjectState (const String& objectId)
{
return pos;
return getDocument().findDrawableState (objectId, false);
}
const SelectedItems::ItemType findObjectIdAt (const Point<int>& position)
@@ -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 mouseDoubleClick (const MouseEvent& e)
{
}
void updatePosition (const RelativePoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder)
void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder)
{
const Point<float> p (point.resolve (nameFinder));
setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 5, 5));
const Point<float> p (point.getPosition().resolve (nameFinder));
setBoundsInTargetSpace (Rectangle<int> (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 7, 7));
const bool nowSelected = canvas->getSelection().isSelected (selectionId);
if (selected != nowSelected)
{
selected = nowSelected;
repaint();
}
}
private:
ValueTree drawableState;
int controlPointNum;
bool isDragging, mouseDownResult, selected;
String selectionId;
};
void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray<OverlayItemComponent>& comps)
void updateControlPointComponents (Component* parent, OwnedArray<OverlayItemComponent>& comps)
{
if (drawable == 0)
{
@@ -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,84 @@ public:
}
}
bool isControlPointId (const String& itemId)
{
return itemId.containsChar ('/');
}
static const String getControlPointId (const ValueTree& drawableState, int index)
{
return Drawable::ValueTreeWrapperBase (drawableState).getID() + "/" + String (index);
}
//==============================================================================
class DragOperation : public EditorDragOperation
class ObjectDragOperation : public EditorDragOperation
{
public:
DragOperation (DrawableEditorCanvas* canvas_,
const MouseEvent& e, const Point<int>& mousePos,
Component* snapGuideParentComp_,
const ResizableBorderComponent::Zone& zone_)
: EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_)
ObjectDragOperation (DrawableEditorCanvas* canvas_,
const MouseEvent& e, const Point<int>& mousePos,
Component* snapGuideParentComp_,
const ResizableBorderComponent::Zone& zone_)
: EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), drawableCanvas (canvas_)
{
}
~DragOperation()
~ObjectDragOperation() {}
protected:
DrawableDocument& getDocument() throw() { return drawableCanvas->getDocument(); }
void getSnapPointsX (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); }
void getSnapPointsY (Array<float>& points, bool /*includeCentre*/) { points.add (0.0f); }
UndoManager& getUndoManager() { return *getDocument().getUndoManager(); }
void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps)
{
getUndoManager().beginNewTransaction();
drawableCanvas->getObjectPositionDependencies (state, deps);
}
const Rectangle<float> getObjectPosition (const ValueTree& state)
{
return drawableCanvas->getObjectPositionFloat (state);
}
void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds)
{
drawableCanvas->setObjectPositionFloat (state, newBounds);
}
float getMarkerPosition (const ValueTree& marker, bool isX)
{
return 0;
}
private:
DrawableEditorCanvas* drawableCanvas;
};
//==============================================================================
class ControlPointDragOperation : public EditorDragOperation
{
public:
ControlPointDragOperation (DrawableEditorCanvas* canvas_,
const DrawableTypeInstance& drawableItem_,
Drawable* drawable_,
const MouseEvent& e, const Point<int>& mousePos,
Component* snapGuideParentComp_,
const ResizableBorderComponent::Zone& zone_)
: EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_),
drawableCanvas (canvas_), drawableItem (drawableItem_), drawable (drawable_)
{
drawableItem.getAllControlPoints (points);
}
~ControlPointDragOperation() {}
OwnedArray <ControlPoint> points;
protected:
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,55 +462,91 @@ public:
void getObjectDependencies (const ValueTree& state, Array<ValueTree>& deps)
{
static_cast <DrawableEditorCanvas*> (canvas)->getObjectPositionDependencies (state, deps);
drawableCanvas->getObjectPositionDependencies (drawableItem.getState(), deps);
}
const Rectangle<float> getObjectPosition (const ValueTree& state)
{
return static_cast <DrawableEditorCanvas*> (canvas)->getObjectPositionFloat (state);
int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue();
ControlPoint* cp = points[index];
if (cp == 0)
return Rectangle<float>();
Point<float> p (cp->getPosition().resolve (drawable->getParent()));
return Rectangle<float> (p, p);
}
void setObjectPosition (ValueTree& state, const Rectangle<float>& newBounds)
{
static_cast <DrawableEditorCanvas*> (canvas)->setObjectPositionFloat (state, newBounds);
int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue();
ControlPoint* cp = points[index];
if (cp != 0)
{
RelativePoint p (cp->getPosition());
p.moveToAbsolute (newBounds.getPosition(), drawable->getParent());
cp->setPosition (p, getDocument().getUndoManager());
}
}
float getMarkerPosition (const ValueTree& marker, bool isX)
{
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);
d->initialise (selected, unselected);
return d;
}
DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode());
UndoManager& getUndoManager()
{
return *getDocument().getUndoManager();
}
for (int i = mainGroup.getNumDrawables(); --i >= 0;)
{
const ValueTree v (mainGroup.getDrawableState (i));
DrawableEditor& getEditor() throw() { return editor; }
DrawableDocument& getDocument() throw() { return editor.getDocument(); }
if (editor.getSelection().isSelected (v[Drawable::ValueTreeWrapperBase::idProperty]))
selected.add (v);
else
unselected.add (v);
}
}
drag->initialise (selected, unselected);
return drag;
}
void timerCallback()
{


+ 45
- 11
extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp View File

@@ -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();
}
}


+ 10
- 2
extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h View File

@@ -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;


+ 1
- 1
extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp View File

@@ -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()


+ 62
- 21
extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h View File

@@ -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:


+ 9
- 0
extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp View File

@@ -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();
}

+ 171
- 177
juce_amalgamated.cpp View File

@@ -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
{
return state [nonZeroWinding];
}

void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager)
{
state.setProperty (nonZeroWinding, b, undoManager);
}

const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move");
const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close");
const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line");
const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad");
const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic");

DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_)
: state (state_)
{
}

DrawablePath::ValueTreeWrapper::Element::~Element()
{
RelativePointPath newPath (state [path]);
p.swapWith (newPath);
}

void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager)
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw()
{
state.setProperty (path, newPath, undoManager);
const Identifier i (state.getType());
if (i == startSubPathElement || i == lineToElement) return 1;
if (i == quadraticToElement) return 2;
if (i == cubicToElement) return 3;
return 0;
}

const RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const
{
jassert (index >= 0 && index < getNumControlPoints());
return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString());
}

void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager)
{
jassert (index >= 0 && index < getNumControlPoints());
return state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager);
}

const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*)
@@ -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];
switch (i.elementType)
{
case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break;
case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break;
case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break;
case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break;
case Path::Iterator::closePath: elements.add (new CloseSubPath()); break;
default: jassertfalse; break;
}
}
}

if (firstChar == 0)
break;
void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager)
{
DrawablePath::ValueTreeWrapper wrapper (state);
wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager);

const juce_wchar secondChar = s[i + 1];
ValueTree pathTree (wrapper.getPathState());
pathTree.removeAllChildren (undoManager);

if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar))
{
if (firstChar == 'm' || firstChar == 'l')
{
++i;
marker = firstChar;
numValues = 1;
}
else if (firstChar == 'q')
{
++i;
marker = firstChar;
numValues = 2;
}
else if (firstChar == 'c')
{
++i;
marker = firstChar;
numValues = 3;
}
else if (firstChar == 'z')
{
++i;
marker = 'm';
numValues = 2;
elements.add (new CloseSubPath());
continue;
}
else if (firstChar == 'a')
{
++i;
usesNonZeroWinding = false;
continue;
}
}
for (int i = 0; i < elements.size(); ++i)
pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager);
}

if (firstChar == '#')
++i;
void RelativePointPath::parse (const ValueTree& state)
{
DrawablePath::ValueTreeWrapper wrapper (state);
usesNonZeroWinding = wrapper.usesNonZeroWinding();
RelativePoint points[3];

const ValueTree pathTree (wrapper.getPathState());
const int num = pathTree.getNumChildren();
for (int i = 0; i < num; ++i)
{
const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i));

for (int j = 0; j < numValues; ++j)
const int numCps = e.getNumControlPoints();
for (int j = 0; j < numCps; ++j)
{
const RelativeCoordinate x (RelativeCoordinateHelpers::readNextCoordinate (s, i, true));
const RelativeCoordinate y (RelativeCoordinateHelpers::readNextCoordinate (s, i, false));
points[j] = RelativePoint (x, y);
points[j] = e.getControlPoint (j);
containsDynamicPoints = containsDynamicPoints || points[j].isDynamic();
}

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


+ 52
- 24
juce_amalgamated.h View File

@@ -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);

class Element
{
public:
explicit Element (const ValueTree& state);
~Element();

const Identifier getType() const throw() { return state.getType(); }
int getNumControlPoints() const throw();

const RelativePoint getControlPoint (int index) const;
void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager);

static const Identifier startSubPathElement, closeSubPathElement,
lineToElement, quadraticToElement, cubicToElement;

ValueTree state;
};

ValueTree getPathState();

static const Identifier fill, stroke, jointStyle, capStyle, strokeWidth, path;
static const Identifier fill, stroke, path, jointStyle, capStyle, strokeWidth,
nonZeroWinding, point1, point2, point3;
};

juce_UseDebuggingNewOperator


+ 17
- 0
src/containers/juce_ValueTree.cpp View File

@@ -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;


+ 12
- 1
src/containers/juce_ValueTree.h View File

@@ -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*);


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -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.


+ 1
- 1
src/gui/graphics/colour/juce_ColourGradient.cpp View File

@@ -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);


+ 0
- 26
src/gui/graphics/drawables/juce_Drawable.cpp View File

@@ -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

+ 0
- 5
src/gui/graphics/drawables/juce_Drawable.h View File

@@ -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);
};
//==============================================================================


+ 2
- 12
src/gui/graphics/drawables/juce_DrawableComposite.cpp View File

@@ -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


+ 3
- 1
src/gui/graphics/drawables/juce_DrawableImage.cpp View File

@@ -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);


+ 66
- 14
src/gui/graphics/drawables/juce_DrawablePath.cpp View File

@@ -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
{
return state [nonZeroWinding];
}
void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager)
{
state.setProperty (nonZeroWinding, b, undoManager);
}
const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move");
const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close");
const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line");
const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad");
const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic");
DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_)
: state (state_)
{
}
DrawablePath::ValueTreeWrapper::Element::~Element()
{
RelativePointPath newPath (state [path]);
p.swapWith (newPath);
}
void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager)
int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw()
{
state.setProperty (path, newPath, undoManager);
const Identifier i (state.getType());
if (i == startSubPathElement || i == lineToElement) return 1;
if (i == quadraticToElement) return 2;
if (i == cubicToElement) return 3;
return 0;
}
const RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const
{
jassert (index >= 0 && index < getNumControlPoints());
return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString());
}
void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager)
{
jassert (index >= 0 && index < getNumControlPoints());
return state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager);
}
//==============================================================================
const Rectangle<float> DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*)
{
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;
}


+ 25
- 4
src/gui/graphics/drawables/juce_DrawablePath.h View File

@@ -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;
};
//==============================================================================


+ 85
- 123
src/gui/graphics/geometry/juce_RelativeCoordinate.cpp View File

@@ -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];
switch (i.elementType)
{
case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break;
case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break;
case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break;
case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break;
case Path::Iterator::closePath: elements.add (new CloseSubPath()); break;
default: jassertfalse; break;
}
}
}
if (firstChar == 0)
break;
void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager)
{
DrawablePath::ValueTreeWrapper wrapper (state);
wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager);
const juce_wchar secondChar = s[i + 1];
ValueTree pathTree (wrapper.getPathState());
pathTree.removeAllChildren (undoManager);
if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar))
{
if (firstChar == 'm' || firstChar == 'l')
{
++i;
marker = firstChar;
numValues = 1;
}
else if (firstChar == 'q')
{
++i;
marker = firstChar;
numValues = 2;
}
else if (firstChar == 'c')
{
++i;
marker = firstChar;
numValues = 3;
}
else if (firstChar == 'z')
{
++i;
marker = 'm';
numValues = 2;
elements.add (new CloseSubPath());
continue;
}
else if (firstChar == 'a')
{
++i;
usesNonZeroWinding = false;
continue;
}
}
for (int i = 0; i < elements.size(); ++i)
pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager);
}
if (firstChar == '#')
++i;
void RelativePointPath::parse (const ValueTree& state)
{
DrawablePath::ValueTreeWrapper wrapper (state);
usesNonZeroWinding = wrapper.usesNonZeroWinding();
RelativePoint points[3];
for (int j = 0; j < numValues; ++j)
const ValueTree pathTree (wrapper.getPathState());
const int num = pathTree.getNumChildren();
for (int i = 0; i < num; ++i)
{
const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i));
const int numCps = e.getNumControlPoints();
for (int j = 0; j < numCps; ++j)
{
const RelativeCoordinate x (RelativeCoordinateHelpers::readNextCoordinate (s, i, true));
const RelativeCoordinate y (RelativeCoordinateHelpers::readNextCoordinate (s, i, false));
points[j] = RelativePoint (x, y);
points[j] = e.getControlPoint (j);
containsDynamicPoints = containsDynamicPoints || points[j].isDynamic();
}
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


+ 15
- 13
src/gui/graphics/geometry/juce_RelativeCoordinate.h View File

@@ -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&);
};


+ 1
- 1
src/io/network/juce_URL.cpp View File

@@ -282,7 +282,7 @@ public:
timeOutMs);
if (responseHeaders != 0)
juce_getInternetFileHeaders (handle, *responseHeaders);
juce_getInternetFileHeaders (handle, *responseHeaders);
}
~WebInputStream()


+ 481
- 481
src/native/linux/juce_linux_Network.cpp View File

@@ -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…
Cancel
Save