diff --git a/extras/Jucer (experimental)/Builds/Linux/Makefile b/extras/Jucer (experimental)/Builds/Linux/Makefile index 80c31c32da..8724952953 100644 --- a/extras/Jucer (experimental)/Builds/Linux/Makefile +++ b/extras/Jucer (experimental)/Builds/Linux/Makefile @@ -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" diff --git a/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj b/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj index 5a93e5cc50..6b46d457cd 100644 --- a/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj +++ b/extras/Jucer (experimental)/Builds/MacOSX/The Jucer.xcodeproj/project.pbxproj @@ -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 = ""; }; ADA17383E5554BCDF567AACC = { isa = PBXGroup; children = ( 9DCB32BBD7053ACCB598CE79, @@ -416,6 +419,7 @@ 061C981D0E9FB70DE5A3D32C, FEDBCD721085272D356BBAEB, 268807D7091702D29033CC27, + 91CCD0421DDD432C1C4903D4, 06838545183964D8C1E60530, 048D33BDE7413EFE05D09901, 9DA1913A62297D7111E0A6C9, diff --git a/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj b/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj index 042d820686..d90a3d7b35 100644 --- a/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj +++ b/extras/Jucer (experimental)/Builds/VisualStudio2005/The Jucer.vcproj @@ -154,6 +154,7 @@ + diff --git a/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj b/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj index 286e77a0f2..6a89ecf2b3 100644 --- a/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj +++ b/extras/Jucer (experimental)/Builds/VisualStudio2008/The Jucer.vcproj @@ -154,6 +154,7 @@ + diff --git a/extras/Jucer (experimental)/Jucer.jucer b/extras/Jucer (experimental)/Jucer.jucer index 6e4713feb0..18ffef8c3d 100644 --- a/extras/Jucer (experimental)/Jucer.jucer +++ b/extras/Jucer (experimental)/Jucer.jucer @@ -72,6 +72,8 @@ file="Source/model/Drawable/jucer_DrawableDocument.cpp"/> + diff --git a/extras/Jucer (experimental)/Source/jucer_Headers.h b/extras/Jucer (experimental)/Source/jucer_Headers.h index 4e04d2e3ec..41f17c12eb 100644 --- a/extras/Jucer (experimental)/Source/jucer_Headers.h +++ b/extras/Jucer (experimental)/Source/jucer_Headers.h @@ -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"); } diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp index 64216fce73..370756f969 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp @@ -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 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 & 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& newBounds) -{ - return getHandler()->setBounds (*this, drawable, newBounds); -} - -void DrawableTypeInstance::getAllControlPoints (Array & 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 (Random::getSystemRandom().nextFloat() * 100.0f + 100.0f, + Random::getSystemRandom().nextFloat() * 100.0f + 100.0f))); - if (handler != 0) - { - ValueTree state (handler->createNewInstance (*this, - Point (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)); diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp new file mode 100644 index 0000000000..f4dc27cfff --- /dev/null +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp @@ -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& 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& 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& 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 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 & 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 & 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& 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 (100.0f, 0.0f)), + RelativePoint (approxPosition + Point (0.0f, 100.0f))); + return di.createValueTree (&document); + } + + void createPropertyEditors (DrawableTypeInstance& item, Array & 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 & 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 & 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 (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 & 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& 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 & 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 DrawableTypeInstance::getBounds() +{ + OwnedArray points; + getAllControlPoints (points); + + if (points.size() < 2) + return Rectangle(); + + DrawableTypeInstance parent (document, state.getParent()); + const Point p1 (points.getUnchecked(0)->getPosition().resolve (&parent)); + Rectangle r (p1, points.getUnchecked(1)->getPosition().resolve (&parent)); + + for (int i = 2; i < points.size(); ++i) + r = r.getUnion (Rectangle (p1, points.getUnchecked(i)->getPosition().resolve (&parent))); + + return r; +} + +void DrawableTypeInstance::setBounds (Drawable* drawable, const Rectangle& newBounds) +{ + return getHandler()->setBounds (*this, drawable, newBounds); +} + +void DrawableTypeInstance::getAllControlPoints (OwnedArray & points) +{ + return getHandler()->getAllControlPoints (*this, points); +} diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h index f296275357..e4b9b828fd 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h @@ -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 & props); + const Rectangle getBounds(); void setBounds (Drawable* drawable, const Rectangle& newBounds); - void getAllControlPoints (Array & points); + void getAllControlPoints (OwnedArray & 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& approxPosition) = 0; virtual void createPropertyEditors (DrawableTypeInstance& item, Array & props) = 0; virtual void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) = 0; - virtual void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle& newBounds) = 0; - virtual void getAllControlPoints (DrawableTypeInstance& item, Array & points) = 0; + virtual void getAllControlPoints (DrawableTypeInstance& item, OwnedArray & 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& oldBounds, Rectangle newBounds, - RelativeCoordinate::NamedCoordinateFinder* nameFinder) + void setBounds (DrawableTypeInstance& item, Drawable* drawable, Rectangle newBounds) { + const Rectangle 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 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 p (points[i].resolve (nameFinder)); + ControlPoint* cp = points.getUnchecked(i); + RelativePoint point (cp->getPosition()); + const Point p (point.resolve (nameFinder)); - points[i].moveToAbsolute (Point ((float) (xOffset + xScale * p.getX()), - (float) (yOffset + yScale * p.getY())), nameFinder); + point.moveToAbsolute (Point ((float) (xOffset + xScale * p.getX()), + (float) (yOffset + yScale * p.getY())), nameFinder); + + cp->setPosition (point, undoManager); } - - return true; } private: @@ -124,200 +143,28 @@ private: DrawableTypeHandler& operator= (const DrawableTypeHandler&); }; -//============================================================================== -class DrawablePathHandler : public DrawableTypeHandler -{ -public: - DrawablePathHandler() : DrawableTypeHandler ("Polygon", DrawablePath::valueTreeType, true) {} - ~DrawablePathHandler() {} - - const ValueTree createNewInstance (DrawableDocument& document, const Point& 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 & 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& newBounds) - { - DrawablePath::ValueTreeWrapper wrapper (item.getState()); - - RelativePointPath path; - wrapper.getPath (path); - - Array 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 & points) - { - DrawablePath::ValueTreeWrapper wrapper (item.getState()); - - RelativePointPath path; - wrapper.getPath (path); - - for (int i = 0; i < path.elements.size(); ++i) - { - int numPoints; - RelativePoint* elementPoints = path.elements.getUnchecked(i)->getControlPoints (numPoints); - - for (int j = 0; j < numPoints; ++j) - points.add (elementPoints[j]); - } - } -}; //============================================================================== -class DrawableImageHandler : public DrawableTypeHandler +class DrawableTypeManager : public DeletedAtShutdown { public: - DrawableImageHandler() : DrawableTypeHandler ("Image", DrawableImage::valueTreeType, true) {} - ~DrawableImageHandler() {} + DrawableTypeManager(); + ~DrawableTypeManager(); - const ValueTree createNewInstance (DrawableDocument& document, const Point& approxPosition) - { - Image tempImage (Image::ARGB, 100, 100, true); + juce_DeclareSingleton_SingleThreaded_Minimal (DrawableTypeManager); - { - Graphics g (tempImage); - g.fillAll (Colours::grey.withAlpha (0.3f)); - g.setColour (Colours::red); - g.setFont (40.0f); - g.drawText ("?", 0, 0, 100, 100, Justification::centred, false); - } + //============================================================================== + int getNumHandlers() const { return handlers.size(); } + DrawableTypeHandler* getHandler (const int index) const { return handlers [index]; } - DrawableImage di; - di.setTransform (RelativePoint (approxPosition), - RelativePoint (approxPosition + Point (100.0f, 0.0f)), - RelativePoint (approxPosition + Point (0.0f, 100.0f))); - return di.createValueTree (&document); - } + DrawableTypeHandler* getHandlerFor (const Identifier& type); - void createPropertyEditors (DrawableTypeInstance& item, Array & props) - { - } + const StringArray getNewItemList(); + const ValueTree createNewItem (const int index, DrawableDocument& document, const Point& approxPosition); - void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) - { - } - - void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle& newBounds) - { - DrawableImage::ValueTreeWrapper wrapper (item.getState()); - - RelativePoint points[3] = { wrapper.getTargetPositionForTopLeft(), - wrapper.getTargetPositionForTopRight(), - wrapper.getTargetPositionForBottomLeft() }; - - if (rescalePoints (points, 3, drawable->getBounds(), newBounds, drawable->getParent())) - { - UndoManager* undoManager = item.getDocument().getUndoManager(); - wrapper.setTargetPositionForTopLeft (points[0], undoManager); - wrapper.setTargetPositionForTopRight (points[1], undoManager); - wrapper.setTargetPositionForBottomLeft (points[2], undoManager); - } - } - - void getAllControlPoints (DrawableTypeInstance& item, Array & points) - { - DrawableImage::ValueTreeWrapper wrapper (item.getState()); - - points.add (wrapper.getTargetPositionForTopLeft()); - points.add (wrapper.getTargetPositionForTopRight()); - points.add (wrapper.getTargetPositionForBottomLeft()); - } +private: + OwnedArray handlers; }; -//============================================================================== -class DrawableCompositeHandler : public DrawableTypeHandler -{ -public: - DrawableCompositeHandler() : DrawableTypeHandler ("Group", DrawableComposite::valueTreeType, false) {} - ~DrawableCompositeHandler() {} - - const ValueTree createNewInstance (DrawableDocument& document, const Point& approxPosition) - { - return ValueTree::invalid; - } - - void createPropertyEditors (DrawableTypeInstance& item, Array & props) - { - } - - void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) - { - } - - void setBounds (DrawableTypeInstance& item, Drawable* drawable, const Rectangle& 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 & points) - { - DrawableComposite::ValueTreeWrapper wrapper (item.getState()); - - points.add (wrapper.getTargetPositionForOrigin()); - points.add (wrapper.getTargetPositionForX1Y0()); - points.add (wrapper.getTargetPositionForX0Y1()); - } -}; #endif // __JUCER_DRAWABLETYPEHANDLER_H_7FB02E2F__ diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h index a1ee614db0..2b4a58ef56 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h @@ -146,7 +146,7 @@ public: return getDocument().getCoordsFor (state); } - void updateExtraComponentsForObject (const ValueTree& state, Component* parent, OwnedArray& existingComps) + void updateControlPointComponents (Component* parent, OwnedArray& existingComps) { } @@ -184,7 +184,6 @@ public: ~DragOperation() { - getUndoManager().beginNewTransaction(); } protected: diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h index 9a8af440f6..c9786bac85 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h @@ -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 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& newBounds) {} bool canResizeCanvas() const { return false; } - MarkerListBase& getMarkerList (bool isX) + //============================================================================== + const ValueTree getObjectState (const String& objectId) { - return getDocument().getMarkerList (isX); - } - - double limitMarkerPosition (double pos) - { - return pos; + return getDocument().findDrawableState (objectId, false); } const SelectedItems::ItemType findObjectIdAt (const Point& 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& deps) { DrawableDocument& doc = getDocument(); DrawableTypeInstance item (doc, state); - Array points; + OwnedArray points; item.getAllControlPoints (points); StringArray anchors; for (int i = 0; i < points.size(); ++i) { - anchors.addIfNotAlreadyThere (points.getReference(i).x.getAnchorName1()); - anchors.addIfNotAlreadyThere (points.getReference(i).x.getAnchorName2()); - anchors.addIfNotAlreadyThere (points.getReference(i).y.getAnchorName1()); - anchors.addIfNotAlreadyThere (points.getReference(i).y.getAnchorName2()); + const RelativePoint p (points.getUnchecked(i)->getPosition()); + anchors.addIfNotAlreadyThere (p.x.getAnchorName1()); + anchors.addIfNotAlreadyThere (p.x.getAnchorName2()); + anchors.addIfNotAlreadyThere (p.y.getAnchorName1()); + anchors.addIfNotAlreadyThere (p.y.getAnchorName2()); } for (int i = 0; i < anchors.size(); ++i) @@ -207,12 +215,15 @@ public: return RelativeRectangle(); } + //============================================================================== class ControlPointComponent : public OverlayItemComponent { public: - ControlPointComponent (DrawableEditorCanvas* canvas) - : OverlayItemComponent (canvas) + ControlPointComponent (DrawableEditorCanvas* canvas, const ValueTree& drawableState_, int controlPointNum_) + : OverlayItemComponent (canvas), drawableState (drawableState_), + controlPointNum (controlPointNum_), isDragging (false), mouseDownResult (false), selected (false) { + selectionId = getControlPointId (drawableState, controlPointNum); } ~ControlPointComponent() @@ -221,29 +232,79 @@ public: void paint (Graphics& g) { - g.fillAll (Colours::pink); + g.setColour (Colour (selected ? 0xaaaaaaaa : 0xaa333333)); + g.drawRect (0, 0, getWidth(), getHeight()); + + g.setColour (Colour (selected ? 0xaa000000 : 0x99ffffff)); + g.fillRect (1, 1, getWidth() - 2, getHeight() - 2); } void mouseDown (const MouseEvent& e) { + isDragging = false; + + if (e.mods.isPopupMenu()) + { + canvas->showPopupMenu (true); + } + else + { + mouseDownResult = canvas->getSelection().addToSelectionOnMouseDown (selectionId, e.mods); + } } void mouseDrag (const MouseEvent& e) { + if (! (isDragging || e.mouseWasClicked() || e.mods.isPopupMenu())) + { + canvas->getSelection().addToSelectionOnMouseUp (selectionId, e.mods, true, mouseDownResult); + + isDragging = true; + canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), + ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); + } + + if (isDragging) + { + canvas->continueDrag (e.getEventRelativeTo (getParentComponent())); + autoScrollForMouseEvent (e); + } } void mouseUp (const MouseEvent& e) { + if (isDragging) + { + canvas->endDrag (e.getEventRelativeTo (getParentComponent())); + } } - void updatePosition (const RelativePoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) + void mouseDoubleClick (const MouseEvent& e) { - const Point p (point.resolve (nameFinder)); - setBoundsInTargetSpace (Rectangle (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 5, 5)); } + + void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) + { + const Point p (point.getPosition().resolve (nameFinder)); + setBoundsInTargetSpace (Rectangle (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& comps) + void updateControlPointComponents (Component* parent, OwnedArray& comps) { if (drawable == 0) { @@ -251,11 +312,11 @@ public: return; } - DrawableTypeInstance item (getDocument(), state); - Array points; + DrawableTypeInstance item (getDocument(), controlPointEditingTarget); + OwnedArray points; item.getAllControlPoints (points); - Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (state).getID()); + Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); DrawableComposite* parentDrawable = d->getParent(); comps.removeRange (points.size(), comps.size()); @@ -269,15 +330,27 @@ public: if (c == 0) { - c = new ControlPointComponent (this); + c = new ControlPointComponent (this, controlPointEditingTarget, i); comps.set (i, c); parent->addAndMakeVisible (c); } - c->updatePosition (points.getReference(i), parentDrawable); + c->updatePosition (*points.getUnchecked(i), parentDrawable); } } + //============================================================================== + MarkerListBase& getMarkerList (bool isX) + { + return getDocument().getMarkerList (isX); + } + + double limitMarkerPosition (double pos) + { + return pos; + } + + //============================================================================== SelectedItems& getSelection() { return editor.getSelection(); @@ -303,25 +376,32 @@ public: } } + bool isControlPointId (const String& itemId) + { + return itemId.containsChar ('/'); + } + + static const String getControlPointId (const ValueTree& drawableState, int index) + { + return Drawable::ValueTreeWrapperBase (drawableState).getID() + "/" + String (index); + } + //============================================================================== - class DragOperation : public EditorDragOperation + class ObjectDragOperation : public EditorDragOperation { public: - DragOperation (DrawableEditorCanvas* canvas_, - const MouseEvent& e, const Point& mousePos, - Component* snapGuideParentComp_, - const ResizableBorderComponent::Zone& zone_) - : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_) + ObjectDragOperation (DrawableEditorCanvas* canvas_, + const MouseEvent& e, const Point& mousePos, + Component* snapGuideParentComp_, + const ResizableBorderComponent::Zone& zone_) + : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), drawableCanvas (canvas_) { } - ~DragOperation() - { - getUndoManager().beginNewTransaction(); - } + ~ObjectDragOperation() {} protected: - DrawableDocument& getDocument() throw() { return static_cast (canvas)->getDocument(); } + DrawableDocument& getDocument() throw() { return drawableCanvas->getDocument(); } void getSnapPointsX (Array& points, bool /*includeCentre*/) { points.add (0.0f); } void getSnapPointsY (Array& points, bool /*includeCentre*/) { points.add (0.0f); } @@ -330,56 +410,144 @@ public: void getObjectDependencies (const ValueTree& state, Array& deps) { - static_cast (canvas)->getObjectPositionDependencies (state, deps); + drawableCanvas->getObjectPositionDependencies (state, deps); } const Rectangle getObjectPosition (const ValueTree& state) { - return static_cast (canvas)->getObjectPositionFloat (state); + return drawableCanvas->getObjectPositionFloat (state); } void setObjectPosition (ValueTree& state, const Rectangle& newBounds) { - static_cast (canvas)->setObjectPositionFloat (state, newBounds); + drawableCanvas->setObjectPositionFloat (state, newBounds); } float getMarkerPosition (const ValueTree& marker, bool isX) { return 0; } + + private: + DrawableEditorCanvas* drawableCanvas; }; + //============================================================================== + class ControlPointDragOperation : public EditorDragOperation + { + public: + ControlPointDragOperation (DrawableEditorCanvas* canvas_, + const DrawableTypeInstance& drawableItem_, + Drawable* drawable_, + const MouseEvent& e, const Point& mousePos, + Component* snapGuideParentComp_, + const ResizableBorderComponent::Zone& zone_) + : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), + drawableCanvas (canvas_), drawableItem (drawableItem_), drawable (drawable_) + { + drawableItem.getAllControlPoints (points); + } + + ~ControlPointDragOperation() {} + + OwnedArray points; + + protected: + DrawableDocument& getDocument() throw() { return drawableCanvas->getDocument(); } + + void getSnapPointsX (Array& points, bool /*includeCentre*/) { points.add (0.0f); } + void getSnapPointsY (Array& points, bool /*includeCentre*/) { points.add (0.0f); } + + UndoManager& getUndoManager() { return *getDocument().getUndoManager(); } + + void getObjectDependencies (const ValueTree& state, Array& deps) + { + drawableCanvas->getObjectPositionDependencies (drawableItem.getState(), deps); + } + + const Rectangle getObjectPosition (const ValueTree& state) + { + int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); + ControlPoint* cp = points[index]; + if (cp == 0) + return Rectangle(); + + Point p (cp->getPosition().resolve (drawable->getParent())); + return Rectangle (p, p); + } + + void setObjectPosition (ValueTree& state, const Rectangle& 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 selected, unselected; + EditorDragOperation* drag = 0; - DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); - - for (int i = mainGroup.getNumDrawables(); --i >= 0;) + if (isControlPointMode()) { - const ValueTree v (mainGroup.getDrawableState (i)); + Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); + DrawableTypeInstance item (getDocument(), controlPointEditingTarget); - if (editor.getSelection().isSelected (v[Drawable::ValueTreeWrapperBase::idProperty])) - selected.add (v); - else - unselected.add (v); + ControlPointDragOperation* cpd = new ControlPointDragOperation (this, item, d, e, e.getPosition() - origin, snapGuideParentComponent, zone); + drag = cpd; + + for (int i = 0; i < cpd->points.size(); ++i) + { + const String pointId (getControlPointId (item.getState(), i)); + + ValueTree v (Ids::controlPoint); + v.setProperty (Ids::id_, pointId, 0); + + if (editor.getSelection().isSelected (pointId)) + selected.add (v); + else + unselected.add (v); + } + } + else + { + drag = new ObjectDragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); + + DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); + + for (int i = mainGroup.getNumDrawables(); --i >= 0;) + { + const ValueTree v (mainGroup.getDrawableState (i)); + + if (editor.getSelection().isSelected (v[Drawable::ValueTreeWrapperBase::idProperty])) + selected.add (v); + else + unselected.add (v); + } } - d->initialise (selected, unselected); - return d; + drag->initialise (selected, unselected); + return drag; } - UndoManager& getUndoManager() - { - return *getDocument().getUndoManager(); - } - - DrawableEditor& getEditor() throw() { return editor; } - DrawableDocument& getDocument() throw() { return editor.getDocument(); } - void timerCallback() { stopTimer(); diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp index 8797350cbe..ce536eeca1 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp @@ -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 sizeGuides; bool isDragging; - OwnedArray extraEditorComps; const Rectangle 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 resizers; OwnedArray markersX, markersY; + OwnedArray 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 & markers, const bool isX) { MarkerListBase& markerList = canvas->getMarkerList (isX); @@ -827,6 +844,21 @@ const Rectangle 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(); } } diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h index aaf04bc51c..5d7a306303 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h @@ -88,6 +88,7 @@ public: virtual void deselectNonDraggableObjects() = 0; virtual void findLassoItemsInArea (Array & itemsFound, const Rectangle& 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& existingComps) = 0; + virtual void updateControlPointComponents (Component* parent, + OwnedArray& existingComps) = 0; protected: //============================================================================== const BorderSize border; Point origin; double scaleFactor; + ValueTree controlPointEditingTarget; friend class OverlayItemComponent; class ResizeFrame; diff --git a/extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp b/extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp index dd5d4b8afd..8ae7cea40a 100644 --- a/extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp +++ b/extras/Jucer (experimental)/Source/ui/jucer_MainWindow.cpp @@ -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() diff --git a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h index 88add5bdf3..2d53638a5d 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h @@ -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: diff --git a/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp b/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp index 97562d450c..4fb15c2c33 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp +++ b/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.cpp @@ -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 (getParentComponent()); + jassert (p != 0); + return p->getDefaultGradient(); +} diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index cd9ee73235..fd169a6eed 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -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 (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 DrawableImage::refreshFromValueTree (const ValueTree& tre || controlPoints[1] != newControlPoint[1] || controlPoints[2] != newControlPoint[2]) { + const Rectangle damage (getBounds()); + opacity = newOpacity; overlayColour = newOverlayColour; controlPoints[0] = newControlPoint[0]; @@ -84934,7 +84917,7 @@ const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tre image = newImage; } - return getBounds(); + return damage.getUnion (getBounds()); } ImageCache::release (newImage); @@ -85118,12 +85101,16 @@ Drawable* DrawablePath::createCopy() const const Identifier DrawablePath::valueTreeType ("Path"); -const Identifier DrawablePath::ValueTreeWrapper::fill ("fill"); -const Identifier DrawablePath::ValueTreeWrapper::stroke ("stroke"); +const Identifier DrawablePath::ValueTreeWrapper::fill ("Fill"); +const Identifier DrawablePath::ValueTreeWrapper::stroke ("Stroke"); +const Identifier DrawablePath::ValueTreeWrapper::path ("Path"); const Identifier DrawablePath::ValueTreeWrapper::jointStyle ("jointStyle"); const Identifier DrawablePath::ValueTreeWrapper::capStyle ("capStyle"); const Identifier DrawablePath::ValueTreeWrapper::strokeWidth ("strokeWidth"); -const Identifier DrawablePath::ValueTreeWrapper::path ("path"); +const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding"); +const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1"); +const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2"); +const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3"); DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) : ValueTreeWrapperBase (state_) @@ -85131,6 +85118,11 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) jassert (state.hasType (valueTreeType)); } +ValueTree DrawablePath::ValueTreeWrapper::getPathState() +{ + return state.getOrCreateChildWithName (path, 0); +} + ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() { ValueTree v (state.getChildWithName (fill)); @@ -85159,7 +85151,8 @@ const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate:: void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2, UndoManager* undoManager) { - replaceFillType (fill, newFill, gp1, gp2, undoManager); + ValueTree v (state.getOrCreateChildWithName (fill, undoManager)); + writeFillType (v, newFill, gp1, gp2, undoManager); } const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const @@ -85170,7 +85163,8 @@ const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2, UndoManager* undoManager) { - replaceFillType (stroke, newFill, gp1, gp2, undoManager); + ValueTree v (state.getOrCreateChildWithName (stroke, undoManager)); + writeFillType (v, newFill, gp1, gp2, undoManager); } const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const @@ -85196,15 +85190,50 @@ void DrawablePath::ValueTreeWrapper::setStrokeType (const PathStrokeType& newStr ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); } -void DrawablePath::ValueTreeWrapper::getPath (RelativePointPath& p) const +bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const { - RelativePointPath newPath (state [path]); - p.swapWith (newPath); + return state [nonZeroWinding]; } -void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager) +void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager) { - state.setProperty (path, newPath, undoManager); + state.setProperty (nonZeroWinding, b, undoManager); +} + +const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move"); +const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close"); +const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line"); +const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); +const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); + +DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) + : state (state_) +{ +} + +DrawablePath::ValueTreeWrapper::Element::~Element() +{ +} + +int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw() +{ + const Identifier i (state.getType()); + if (i == startSubPathElement || i == lineToElement) return 1; + if (i == quadraticToElement) return 2; + if (i == cubicToElement) return 3; + return 0; +} + +const RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const +{ + jassert (index >= 0 && index < getNumControlPoints()); + return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString()); +} + +void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager) +{ + jassert (index >= 0 && index < getNumControlPoints()); + return state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager); } const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*) @@ -85232,8 +85261,7 @@ const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree const PathStrokeType newStroke (v.getStrokeType()); - ScopedPointer newRelativePath (new RelativePointPath()); - v.getPath (*newRelativePath); + ScopedPointer 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& absolutePoint) { } +RelativePoint::RelativePoint (const float x_, const float y_) + : x (x_, true), y (y_, false) +{ +} + RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_) : x (x_), y (y_) { @@ -92911,88 +92949,83 @@ RelativePointPath::RelativePointPath (const RelativePointPath& other) : usesNonZeroWinding (true), containsDynamicPoints (false) { - parseString (other.toString()); + ValueTree state (DrawablePath::valueTreeType); + writeTo (state, 0); + parse (state); } -RelativePointPath::RelativePointPath (const String& s) +RelativePointPath::RelativePointPath (const ValueTree& drawable) : usesNonZeroWinding (true), containsDynamicPoints (false) { - parseString (s); + parse (drawable); } -void RelativePointPath::parseString (const String& s) +RelativePointPath::RelativePointPath (const Path& path) { - int i = 0; - juce_wchar marker = 'm'; - int numValues = 2; - RelativePoint points [3]; + usesNonZeroWinding = path.isUsingNonZeroWinding(); - for (;;) + Path::Iterator i (path); + + while (i.next()) { - RelativeCoordinateHelpers::skipWhitespace (s, i); - const juce_wchar firstChar = s[i]; - - if (firstChar == 0) - break; - - const juce_wchar secondChar = s[i + 1]; - - if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar)) + switch (i.elementType) { - if (firstChar == 'm' || firstChar == 'l') - { - ++i; - marker = firstChar; - numValues = 1; - } - else if (firstChar == 'q') - { - ++i; - marker = firstChar; - numValues = 2; - } - else if (firstChar == 'c') - { - ++i; - marker = firstChar; - numValues = 3; - } - else if (firstChar == 'z') - { - ++i; - marker = 'm'; - numValues = 2; - elements.add (new CloseSubPath()); - continue; - } - else if (firstChar == 'a') - { - ++i; - usesNonZeroWinding = false; - continue; - } + case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break; + case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break; + case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break; + case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break; + case Path::Iterator::closePath: elements.add (new CloseSubPath()); break; + default: jassertfalse; break; } + } +} - if (firstChar == '#') - ++i; +void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager) +{ + DrawablePath::ValueTreeWrapper wrapper (state); + wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager); - for (int j = 0; j < numValues; ++j) + ValueTree pathTree (wrapper.getPathState()); + pathTree.removeAllChildren (undoManager); + + for (int i = 0; i < elements.size(); ++i) + pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager); +} + +void RelativePointPath::parse (const ValueTree& state) +{ + DrawablePath::ValueTreeWrapper wrapper (state); + usesNonZeroWinding = wrapper.usesNonZeroWinding(); + RelativePoint points[3]; + + const ValueTree pathTree (wrapper.getPathState()); + const int num = pathTree.getNumChildren(); + for (int i = 0; i < num; ++i) + { + const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); + + const int numCps = e.getNumControlPoints(); + for (int j = 0; j < numCps; ++j) { - const RelativeCoordinate x (RelativeCoordinateHelpers::readNextCoordinate (s, i, true)); - const RelativeCoordinate y (RelativeCoordinateHelpers::readNextCoordinate (s, i, false)); - points[j] = RelativePoint (x, y); + points[j] = e.getControlPoint (j); containsDynamicPoints = containsDynamicPoints || points[j].isDynamic(); } - switch (marker) - { - case 'm': elements.add (new StartSubPath (points[0])); break; - case 'l': elements.add (new LineTo (points[0])); break; - case 'q': elements.add (new QuadraticTo (points[0], points[1])); break; - case 'c': elements.add (new CubicTo (points[0], points[1], points[2])); break; - default: jassertfalse; break; // illegal string format? - } + const Identifier type (e.getType()); + + if (type == DrawablePath::ValueTreeWrapper::Element::startSubPathElement) + elements.add (new StartSubPath (points[0])); + else if (type == DrawablePath::ValueTreeWrapper::Element::closeSubPathElement) + elements.add (new CloseSubPath()); + else if (type == DrawablePath::ValueTreeWrapper::Element::lineToElement) + elements.add (new LineTo (points[0])); + else if (type == DrawablePath::ValueTreeWrapper::Element::quadraticToElement) + elements.add (new QuadraticTo (points[0], points[1])); + else if (type == DrawablePath::ValueTreeWrapper::Element::cubicToElement) + elements.add (new CubicTo (points[0], points[1], points[2])); + else + jassertfalse; } } @@ -93017,27 +93050,6 @@ bool RelativePointPath::containsAnyDynamicPoints() const return containsDynamicPoints; } -const String RelativePointPath::toString() const -{ - ElementType lastType = nullElement; - MemoryOutputStream out; - - if (! usesNonZeroWinding) - out << 'a'; - - for (int i = 0; i < elements.size(); ++i) - { - if (out.getDataSize() > 0) - out << ' '; - - const ElementBase* const e = elements.getUnchecked(i); - e->write (out, lastType); - lastType = e->type; - } - - return out.toUTF8(); -} - RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_) { } @@ -93047,16 +93059,11 @@ RelativePointPath::StartSubPath::StartSubPath (const RelativePoint& pos) { } -void RelativePointPath::StartSubPath::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::StartSubPath::createTree() const { - const String p (startPos.toString()); - - if (lastTypeWritten != startSubPathElement) - out << "m "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) - out << '#'; - - out << p; + ValueTree v (DrawablePath::ValueTreeWrapper::Element::startSubPathElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, startPos.toString(), 0); + return v; } void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const @@ -93076,10 +93083,9 @@ RelativePointPath::CloseSubPath::CloseSubPath() { } -void RelativePointPath::CloseSubPath::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::CloseSubPath::createTree() const { - if (lastTypeWritten != closeSubPathElement) - out << 'z'; + return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement); } void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const @@ -93098,16 +93104,11 @@ RelativePointPath::LineTo::LineTo (const RelativePoint& endPoint_) { } -void RelativePointPath::LineTo::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::LineTo::createTree() const { - const String p (endPoint.toString()); - - if (lastTypeWritten != lineToElement) - out << "l "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) - out << '#'; - - out << p; + ValueTree v (DrawablePath::ValueTreeWrapper::Element::lineToElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, endPoint.toString(), 0); + return v; } void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const @@ -93129,16 +93130,12 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint, controlPoints[1] = endPoint; } -void RelativePointPath::QuadraticTo::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::QuadraticTo::createTree() const { - const String p1 (controlPoints[0].toString()); - - if (lastTypeWritten != quadraticToElement) - out << "q "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) - out << '#'; - - out << p1 << ' ' << controlPoints[1].toString(); + ValueTree v (DrawablePath::ValueTreeWrapper::Element::quadraticToElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); + v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); + return v; } void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const @@ -93162,16 +93159,13 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R controlPoints[2] = endPoint; } -void RelativePointPath::CubicTo::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::CubicTo::createTree() const { - const String p1 (controlPoints[0].toString()); - - if (lastTypeWritten != cubicToElement) - out << "c "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) - out << '#'; - - out << p1 << ' ' << controlPoints[1].toString() << ' ' << controlPoints[2].toString(); + ValueTree v (DrawablePath::ValueTreeWrapper::Element::cubicToElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); + v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); + v.setProperty (DrawablePath::ValueTreeWrapper::point3, controlPoints[2].toString(), 0); + return v; } void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 40142ba5e0..e12f209efa 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -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& absolutePoint); + /** Creates an absolute point, relative to the origin. */ + RelativePoint (float absoluteX, float absoluteY); + /** Creates an absolute point from two coordinates. */ RelativePoint (const RelativeCoordinate& x, const RelativeCoordinate& y); @@ -42246,7 +42260,8 @@ public: RelativePointPath(); RelativePointPath (const RelativePointPath& other); - RelativePointPath (const String& stringVersion); + RelativePointPath (const ValueTree& drawable); + RelativePointPath (const Path& path); ~RelativePointPath(); /** Resolves this points in this path and adds them to a normal Path object. */ @@ -42255,11 +42270,8 @@ public: /** Returns true if the path contains any non-fixed points. */ bool containsAnyDynamicPoints() const; - /** Returns a string version of the path. - This has the same format as Path::toString(), but since it can contain RelativeCoordinate - positions, it can't be parsed by the Path class if any of the points are dynamic. - */ - const String toString() const; + /** Writes the path to this drawable encoding. */ + void writeTo (ValueTree state, UndoManager* undoManager); /** Quickly swaps the contents of this path with another. */ void swapWith (RelativePointPath& other) throw(); @@ -42284,7 +42296,7 @@ public: public: ElementBase (ElementType type); virtual ~ElementBase() {} - virtual void write (OutputStream& out, ElementType lastTypeWritten) const = 0; + virtual const ValueTree createTree() const = 0; virtual void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const = 0; virtual RelativePoint* getControlPoints (int& numPoints) = 0; @@ -42300,7 +42312,7 @@ public: public: StartSubPath (const RelativePoint& pos); ~StartSubPath() {} - void write (OutputStream& out, ElementType lastTypeWritten) const; + const ValueTree createTree() const; void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; RelativePoint* getControlPoints (int& numPoints); @@ -42316,7 +42328,7 @@ public: public: CloseSubPath(); ~CloseSubPath() {} - void write (OutputStream& out, ElementType lastTypeWritten) const; + const ValueTree createTree() const; void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; RelativePoint* getControlPoints (int& numPoints); @@ -42330,7 +42342,7 @@ public: public: LineTo (const RelativePoint& endPoint); ~LineTo() {} - void write (OutputStream& out, ElementType lastTypeWritten) const; + const ValueTree createTree() const; void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; RelativePoint* getControlPoints (int& numPoints); @@ -42346,7 +42358,7 @@ public: public: QuadraticTo (const RelativePoint& controlPoint, const RelativePoint& endPoint); ~QuadraticTo() {} - void write (OutputStream& out, ElementType lastTypeWritten) const; + const ValueTree createTree() const; void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; RelativePoint* getControlPoints (int& numPoints); @@ -42362,7 +42374,7 @@ public: public: CubicTo (const RelativePoint& controlPoint1, const RelativePoint& controlPoint2, const RelativePoint& endPoint); ~CubicTo() {} - void write (OutputStream& out, ElementType lastTypeWritten) const; + const ValueTree createTree() const; void addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const; RelativePoint* getControlPoints (int& numPoints); @@ -42379,7 +42391,7 @@ public: private: bool containsDynamicPoints; - void parseString (const String& s); + void parse (const ValueTree& state); RelativePointPath& operator= (const RelativePointPath&); }; @@ -42601,13 +42613,8 @@ public: const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, UndoManager* undoManager); - protected: ValueTree state; static const Identifier type, gradientPoint1, gradientPoint2, colour, radial, colours; - - void replaceFillType (const Identifier& tag, const FillType& fillType, - const RelativePoint* gradientPoint1, const RelativePoint* gradientPoint2, - UndoManager* undoManager); }; juce_UseDebuggingNewOperator @@ -58599,7 +58606,7 @@ public: /** @internal */ static const Identifier valueTreeType; /** @internal */ - const Identifier getValueTreeType() const { return valueTreeType; } + const Identifier getValueTreeType() const { return valueTreeType; } /** Internally-used class for wrapping a DrawablePath's state into a ValueTree. */ class ValueTreeWrapper : public ValueTreeWrapperBase @@ -58620,10 +58627,31 @@ public: const PathStrokeType getStrokeType() const; void setStrokeType (const PathStrokeType& newStrokeType, UndoManager* undoManager); - void getPath (RelativePointPath& path) const; - void setPath (const String& newPath, UndoManager* undoManager); + bool usesNonZeroWinding() const; + void setUsesNonZeroWinding (bool b, UndoManager* undoManager); - static const Identifier fill, stroke, jointStyle, capStyle, strokeWidth, path; + class Element + { + public: + explicit Element (const ValueTree& state); + ~Element(); + + const Identifier getType() const throw() { return state.getType(); } + int getNumControlPoints() const throw(); + + const RelativePoint getControlPoint (int index) const; + void setControlPoint (int index, const RelativePoint& point, UndoManager* undoManager); + + static const Identifier startSubPathElement, closeSubPathElement, + lineToElement, quadraticToElement, cubicToElement; + + ValueTree state; + }; + + ValueTree getPathState(); + + static const Identifier fill, stroke, path, jointStyle, capStyle, strokeWidth, + nonZeroWinding, point1, point2, point3; }; juce_UseDebuggingNewOperator diff --git a/src/containers/juce_ValueTree.cpp b/src/containers/juce_ValueTree.cpp index a4d1f4d714..064a56c398 100644 --- a/src/containers/juce_ValueTree.cpp +++ b/src/containers/juce_ValueTree.cpp @@ -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 (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; diff --git a/src/containers/juce_ValueTree.h b/src/containers/juce_ValueTree.h index e6067b6ea3..371c2e046e 100644 --- a/src/containers/juce_ValueTree.h +++ b/src/containers/juce_ValueTree.h @@ -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*); diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index f8be6f2b85..2f8550bc1c 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -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. diff --git a/src/gui/graphics/colour/juce_ColourGradient.cpp b/src/gui/graphics/colour/juce_ColourGradient.cpp index f8af3886e3..dc05e5c04e 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.cpp +++ b/src/gui/graphics/colour/juce_ColourGradient.cpp @@ -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); diff --git a/src/gui/graphics/drawables/juce_Drawable.cpp b/src/gui/graphics/drawables/juce_Drawable.cpp index 87f773b372..db22b8538b 100644 --- a/src/gui/graphics/drawables/juce_Drawable.cpp +++ b/src/gui/graphics/drawables/juce_Drawable.cpp @@ -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 diff --git a/src/gui/graphics/drawables/juce_Drawable.h b/src/gui/graphics/drawables/juce_Drawable.h index fe797c5262..e083a59093 100644 --- a/src/gui/graphics/drawables/juce_Drawable.h +++ b/src/gui/graphics/drawables/juce_Drawable.h @@ -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); }; //============================================================================== diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.cpp b/src/gui/graphics/drawables/juce_DrawableComposite.cpp index 9c837100d1..061ce52a67 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.cpp +++ b/src/gui/graphics/drawables/juce_DrawableComposite.cpp @@ -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 diff --git a/src/gui/graphics/drawables/juce_DrawableImage.cpp b/src/gui/graphics/drawables/juce_DrawableImage.cpp index 583a620810..ef13aeb4bd 100644 --- a/src/gui/graphics/drawables/juce_DrawableImage.cpp +++ b/src/gui/graphics/drawables/juce_DrawableImage.cpp @@ -313,6 +313,8 @@ const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tre || controlPoints[1] != newControlPoint[1] || controlPoints[2] != newControlPoint[2]) { + const Rectangle damage (getBounds()); + opacity = newOpacity; overlayColour = newOverlayColour; controlPoints[0] = newControlPoint[0]; @@ -328,7 +330,7 @@ const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tre image = newImage; } - return getBounds(); + return damage.getUnion (getBounds()); } ImageCache::release (newImage); diff --git a/src/gui/graphics/drawables/juce_DrawablePath.cpp b/src/gui/graphics/drawables/juce_DrawablePath.cpp index 98d9e88202..a8f3caf60e 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.cpp +++ b/src/gui/graphics/drawables/juce_DrawablePath.cpp @@ -183,12 +183,16 @@ Drawable* DrawablePath::createCopy() const //============================================================================== const Identifier DrawablePath::valueTreeType ("Path"); -const Identifier DrawablePath::ValueTreeWrapper::fill ("fill"); -const Identifier DrawablePath::ValueTreeWrapper::stroke ("stroke"); +const Identifier DrawablePath::ValueTreeWrapper::fill ("Fill"); +const Identifier DrawablePath::ValueTreeWrapper::stroke ("Stroke"); +const Identifier DrawablePath::ValueTreeWrapper::path ("Path"); const Identifier DrawablePath::ValueTreeWrapper::jointStyle ("jointStyle"); const Identifier DrawablePath::ValueTreeWrapper::capStyle ("capStyle"); const Identifier DrawablePath::ValueTreeWrapper::strokeWidth ("strokeWidth"); -const Identifier DrawablePath::ValueTreeWrapper::path ("path"); +const Identifier DrawablePath::ValueTreeWrapper::nonZeroWinding ("nonZeroWinding"); +const Identifier DrawablePath::ValueTreeWrapper::point1 ("p1"); +const Identifier DrawablePath::ValueTreeWrapper::point2 ("p2"); +const Identifier DrawablePath::ValueTreeWrapper::point3 ("p3"); //============================================================================== DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) @@ -197,6 +201,11 @@ DrawablePath::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_) jassert (state.hasType (valueTreeType)); } +ValueTree DrawablePath::ValueTreeWrapper::getPathState() +{ + return state.getOrCreateChildWithName (path, 0); +} + ValueTree DrawablePath::ValueTreeWrapper::getMainFillState() { ValueTree v (state.getChildWithName (fill)); @@ -225,7 +234,8 @@ const FillType DrawablePath::ValueTreeWrapper::getMainFill (RelativeCoordinate:: void DrawablePath::ValueTreeWrapper::setMainFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2, UndoManager* undoManager) { - replaceFillType (fill, newFill, gp1, gp2, undoManager); + ValueTree v (state.getOrCreateChildWithName (fill, undoManager)); + writeFillType (v, newFill, gp1, gp2, undoManager); } const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate::NamedCoordinateFinder* nameFinder) const @@ -236,7 +246,8 @@ const FillType DrawablePath::ValueTreeWrapper::getStrokeFill (RelativeCoordinate void DrawablePath::ValueTreeWrapper::setStrokeFill (const FillType& newFill, const RelativePoint* gp1, const RelativePoint* gp2, UndoManager* undoManager) { - replaceFillType (stroke, newFill, gp1, gp2, undoManager); + ValueTree v (state.getOrCreateChildWithName (stroke, undoManager)); + writeFillType (v, newFill, gp1, gp2, undoManager); } const PathStrokeType DrawablePath::ValueTreeWrapper::getStrokeType() const @@ -262,17 +273,54 @@ void DrawablePath::ValueTreeWrapper::setStrokeType (const PathStrokeType& newStr ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); } -void DrawablePath::ValueTreeWrapper::getPath (RelativePointPath& p) const +bool DrawablePath::ValueTreeWrapper::usesNonZeroWinding() const { - RelativePointPath newPath (state [path]); - p.swapWith (newPath); + return state [nonZeroWinding]; } -void DrawablePath::ValueTreeWrapper::setPath (const String& newPath, UndoManager* undoManager) +void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* undoManager) { - state.setProperty (path, newPath, undoManager); + state.setProperty (nonZeroWinding, b, undoManager); } +const Identifier DrawablePath::ValueTreeWrapper::Element::startSubPathElement ("Move"); +const Identifier DrawablePath::ValueTreeWrapper::Element::closeSubPathElement ("Close"); +const Identifier DrawablePath::ValueTreeWrapper::Element::lineToElement ("Line"); +const Identifier DrawablePath::ValueTreeWrapper::Element::quadraticToElement ("Quad"); +const Identifier DrawablePath::ValueTreeWrapper::Element::cubicToElement ("Cubic"); + +DrawablePath::ValueTreeWrapper::Element::Element (const ValueTree& state_) + : state (state_) +{ +} + +DrawablePath::ValueTreeWrapper::Element::~Element() +{ +} + +int DrawablePath::ValueTreeWrapper::Element::getNumControlPoints() const throw() +{ + const Identifier i (state.getType()); + if (i == startSubPathElement || i == lineToElement) return 1; + if (i == quadraticToElement) return 2; + if (i == cubicToElement) return 3; + return 0; +} + +const RelativePoint DrawablePath::ValueTreeWrapper::Element::getControlPoint (const int index) const +{ + jassert (index >= 0 && index < getNumControlPoints()); + return RelativePoint (state [index == 0 ? point1 : (index == 1 ? point2 : point3)].toString()); +} + +void DrawablePath::ValueTreeWrapper::Element::setControlPoint (const int index, const RelativePoint& point, UndoManager* undoManager) +{ + jassert (index >= 0 && index < getNumControlPoints()); + return state.setProperty (index == 0 ? point1 : (index == 1 ? point2 : point3), point.toString(), undoManager); +} + + +//============================================================================== const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider*) { Rectangle damageRect; @@ -298,8 +346,7 @@ const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree const PathStrokeType newStroke (v.getStrokeType()); - ScopedPointer newRelativePath (new RelativePointPath()); - v.getPath (*newRelativePath); + ScopedPointer 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; } diff --git a/src/gui/graphics/drawables/juce_DrawablePath.h b/src/gui/graphics/drawables/juce_DrawablePath.h index ec8486c490..2c76727e69 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.h +++ b/src/gui/graphics/drawables/juce_DrawablePath.h @@ -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; }; //============================================================================== diff --git a/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp b/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp index 44721648bf..46072fb1c6 100644 --- a/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp +++ b/src/gui/graphics/geometry/juce_RelativeCoordinate.cpp @@ -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& absolutePoint) { } +RelativePoint::RelativePoint (const float x_, const float y_) + : x (x_, true), y (y_, false) +{ +} + RelativePoint::RelativePoint (const RelativeCoordinate& x_, const RelativeCoordinate& y_) : x (x_), y (y_) { @@ -625,88 +631,83 @@ RelativePointPath::RelativePointPath (const RelativePointPath& other) : usesNonZeroWinding (true), containsDynamicPoints (false) { - parseString (other.toString()); + ValueTree state (DrawablePath::valueTreeType); + writeTo (state, 0); + parse (state); } -RelativePointPath::RelativePointPath (const String& s) +RelativePointPath::RelativePointPath (const ValueTree& drawable) : usesNonZeroWinding (true), containsDynamicPoints (false) { - parseString (s); + parse (drawable); } -void RelativePointPath::parseString (const String& s) +RelativePointPath::RelativePointPath (const Path& path) { - int i = 0; - juce_wchar marker = 'm'; - int numValues = 2; - RelativePoint points [3]; + usesNonZeroWinding = path.isUsingNonZeroWinding(); - for (;;) + Path::Iterator i (path); + + while (i.next()) { - RelativeCoordinateHelpers::skipWhitespace (s, i); - const juce_wchar firstChar = s[i]; - - if (firstChar == 0) - break; - - const juce_wchar secondChar = s[i + 1]; - - if (secondChar == 0 || CharacterFunctions::isWhitespace (secondChar)) + switch (i.elementType) { - if (firstChar == 'm' || firstChar == 'l') - { - ++i; - marker = firstChar; - numValues = 1; - } - else if (firstChar == 'q') - { - ++i; - marker = firstChar; - numValues = 2; - } - else if (firstChar == 'c') - { - ++i; - marker = firstChar; - numValues = 3; - } - else if (firstChar == 'z') - { - ++i; - marker = 'm'; - numValues = 2; - elements.add (new CloseSubPath()); - continue; - } - else if (firstChar == 'a') - { - ++i; - usesNonZeroWinding = false; - continue; - } + case Path::Iterator::startNewSubPath: elements.add (new StartSubPath (RelativePoint (i.x1, i.y1))); break; + case Path::Iterator::lineTo: elements.add (new LineTo (RelativePoint (i.x1, i.y1))); break; + case Path::Iterator::quadraticTo: elements.add (new QuadraticTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2))); break; + case Path::Iterator::cubicTo: elements.add (new CubicTo (RelativePoint (i.x1, i.y1), RelativePoint (i.x2, i.y2), RelativePoint (i.x3, i.y3))); break; + case Path::Iterator::closePath: elements.add (new CloseSubPath()); break; + default: jassertfalse; break; } + } +} - if (firstChar == '#') - ++i; +void RelativePointPath::writeTo (ValueTree state, UndoManager* undoManager) +{ + DrawablePath::ValueTreeWrapper wrapper (state); + wrapper.setUsesNonZeroWinding (usesNonZeroWinding, undoManager); - for (int j = 0; j < numValues; ++j) + ValueTree pathTree (wrapper.getPathState()); + pathTree.removeAllChildren (undoManager); + + for (int i = 0; i < elements.size(); ++i) + pathTree.addChild (elements.getUnchecked(i)->createTree(), -1, undoManager); +} + +void RelativePointPath::parse (const ValueTree& state) +{ + DrawablePath::ValueTreeWrapper wrapper (state); + usesNonZeroWinding = wrapper.usesNonZeroWinding(); + RelativePoint points[3]; + + const ValueTree pathTree (wrapper.getPathState()); + const int num = pathTree.getNumChildren(); + for (int i = 0; i < num; ++i) + { + const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); + + const int numCps = e.getNumControlPoints(); + for (int j = 0; j < numCps; ++j) { - const RelativeCoordinate x (RelativeCoordinateHelpers::readNextCoordinate (s, i, true)); - const RelativeCoordinate y (RelativeCoordinateHelpers::readNextCoordinate (s, i, false)); - points[j] = RelativePoint (x, y); + points[j] = e.getControlPoint (j); containsDynamicPoints = containsDynamicPoints || points[j].isDynamic(); } - switch (marker) - { - case 'm': elements.add (new StartSubPath (points[0])); break; - case 'l': elements.add (new LineTo (points[0])); break; - case 'q': elements.add (new QuadraticTo (points[0], points[1])); break; - case 'c': elements.add (new CubicTo (points[0], points[1], points[2])); break; - default: jassertfalse; break; // illegal string format? - } + const Identifier type (e.getType()); + + if (type == DrawablePath::ValueTreeWrapper::Element::startSubPathElement) + elements.add (new StartSubPath (points[0])); + else if (type == DrawablePath::ValueTreeWrapper::Element::closeSubPathElement) + elements.add (new CloseSubPath()); + else if (type == DrawablePath::ValueTreeWrapper::Element::lineToElement) + elements.add (new LineTo (points[0])); + else if (type == DrawablePath::ValueTreeWrapper::Element::quadraticToElement) + elements.add (new QuadraticTo (points[0], points[1])); + else if (type == DrawablePath::ValueTreeWrapper::Element::cubicToElement) + elements.add (new CubicTo (points[0], points[1], points[2])); + else + jassertfalse; } } @@ -731,27 +732,6 @@ bool RelativePointPath::containsAnyDynamicPoints() const return containsDynamicPoints; } -const String RelativePointPath::toString() const -{ - ElementType lastType = nullElement; - MemoryOutputStream out; - - if (! usesNonZeroWinding) - out << 'a'; - - for (int i = 0; i < elements.size(); ++i) - { - if (out.getDataSize() > 0) - out << ' '; - - const ElementBase* const e = elements.getUnchecked(i); - e->write (out, lastType); - lastType = e->type; - } - - return out.toUTF8(); -} - //============================================================================== RelativePointPath::ElementBase::ElementBase (const ElementType type_) : type (type_) { @@ -763,16 +743,11 @@ RelativePointPath::StartSubPath::StartSubPath (const RelativePoint& pos) { } -void RelativePointPath::StartSubPath::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::StartSubPath::createTree() const { - const String p (startPos.toString()); - - if (lastTypeWritten != startSubPathElement) - out << "m "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) - out << '#'; - - out << p; + ValueTree v (DrawablePath::ValueTreeWrapper::Element::startSubPathElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, startPos.toString(), 0); + return v; } void RelativePointPath::StartSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const @@ -793,10 +768,9 @@ RelativePointPath::CloseSubPath::CloseSubPath() { } -void RelativePointPath::CloseSubPath::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::CloseSubPath::createTree() const { - if (lastTypeWritten != closeSubPathElement) - out << 'z'; + return ValueTree (DrawablePath::ValueTreeWrapper::Element::closeSubPathElement); } void RelativePointPath::CloseSubPath::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder*) const @@ -816,16 +790,11 @@ RelativePointPath::LineTo::LineTo (const RelativePoint& endPoint_) { } -void RelativePointPath::LineTo::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::LineTo::createTree() const { - const String p (endPoint.toString()); - - if (lastTypeWritten != lineToElement) - out << "l "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p)) - out << '#'; - - out << p; + ValueTree v (DrawablePath::ValueTreeWrapper::Element::lineToElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, endPoint.toString(), 0); + return v; } void RelativePointPath::LineTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const @@ -848,16 +817,12 @@ RelativePointPath::QuadraticTo::QuadraticTo (const RelativePoint& controlPoint, controlPoints[1] = endPoint; } -void RelativePointPath::QuadraticTo::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::QuadraticTo::createTree() const { - const String p1 (controlPoints[0].toString()); - - if (lastTypeWritten != quadraticToElement) - out << "q "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) - out << '#'; - - out << p1 << ' ' << controlPoints[1].toString(); + ValueTree v (DrawablePath::ValueTreeWrapper::Element::quadraticToElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); + v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); + return v; } void RelativePointPath::QuadraticTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const @@ -882,16 +847,13 @@ RelativePointPath::CubicTo::CubicTo (const RelativePoint& controlPoint1, const R controlPoints[2] = endPoint; } -void RelativePointPath::CubicTo::write (OutputStream& out, ElementType lastTypeWritten) const +const ValueTree RelativePointPath::CubicTo::createTree() const { - const String p1 (controlPoints[0].toString()); - - if (lastTypeWritten != cubicToElement) - out << "c "; - else if (RelativeCoordinateHelpers::couldBeMistakenForPathCommand (p1)) - out << '#'; - - out << p1 << ' ' << controlPoints[1].toString() << ' ' << controlPoints[2].toString(); + ValueTree v (DrawablePath::ValueTreeWrapper::Element::cubicToElement); + v.setProperty (DrawablePath::ValueTreeWrapper::point1, controlPoints[0].toString(), 0); + v.setProperty (DrawablePath::ValueTreeWrapper::point2, controlPoints[1].toString(), 0); + v.setProperty (DrawablePath::ValueTreeWrapper::point3, controlPoints[2].toString(), 0); + return v; } void RelativePointPath::CubicTo::addToPath (Path& path, RelativeCoordinate::NamedCoordinateFinder* coordFinder) const diff --git a/src/gui/graphics/geometry/juce_RelativeCoordinate.h b/src/gui/graphics/geometry/juce_RelativeCoordinate.h index 377ef18054..5beecf5d59 100644 --- a/src/gui/graphics/geometry/juce_RelativeCoordinate.h +++ b/src/gui/graphics/geometry/juce_RelativeCoordinate.h @@ -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& 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&); }; diff --git a/src/io/network/juce_URL.cpp b/src/io/network/juce_URL.cpp index 8df99b3396..269c1e983f 100644 --- a/src/io/network/juce_URL.cpp +++ b/src/io/network/juce_URL.cpp @@ -282,7 +282,7 @@ public: timeOutMs); if (responseHeaders != 0) - juce_getInternetFileHeaders (handle, *responseHeaders); + juce_getInternetFileHeaders (handle, *responseHeaders); } ~WebInputStream() diff --git a/src/native/linux/juce_linux_Network.cpp b/src/native/linux/juce_linux_Network.cpp index 8182792b84..5d8a4aa11b 100644 --- a/src/native/linux/juce_linux_Network.cpp +++ b/src/native/linux/juce_linux_Network.cpp @@ -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 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 (handle); -} - -int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - return s != 0 ? s->read (buffer, bytesToRead) : 0; -} - -int64 juce_getInternetFileContentLength (void* handle) -{ - JUCE_HTTPSocketStream* const s = static_cast (handle); - - if (s != 0) - { - //xxx todo - jassertfalse - } - - return -1; -} - -bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) -{ - JUCE_HTTPSocketStream* const s = static_cast (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 (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 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 (handle); +} + +int juce_readFromInternetFile (void* handle, void* buffer, int bytesToRead) +{ + JUCE_HTTPSocketStream* const s = static_cast (handle); + + return s != 0 ? s->read (buffer, bytesToRead) : 0; +} + +int64 juce_getInternetFileContentLength (void* handle) +{ + JUCE_HTTPSocketStream* const s = static_cast (handle); + + if (s != 0) + { + //xxx todo + jassertfalse + } + + return -1; +} + +bool juce_getInternetFileHeaders (void* handle, StringPairArray& headers) +{ + JUCE_HTTPSocketStream* const s = static_cast (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 (handle); + + return s != 0 ? s->readPosition : 0; +} + +#endif