diff --git a/extras/Jucer (experimental)/Source/jucer_Headers.h b/extras/Jucer (experimental)/Source/jucer_Headers.h index 6d033c1d31..4e04d2e3ec 100644 --- a/extras/Jucer (experimental)/Source/jucer_Headers.h +++ b/extras/Jucer (experimental)/Source/jucer_Headers.h @@ -73,6 +73,7 @@ namespace Ids DECLARE_ID (font); DECLARE_ID (mode); DECLARE_ID (type); + DECLARE_ID (version); DECLARE_ID (position); DECLARE_ID (source); DECLARE_ID (readOnly); @@ -85,6 +86,9 @@ namespace Ids DECLARE_ID (noItemsText); DECLARE_ID (min); DECLARE_ID (max); + DECLARE_ID (width); + DECLARE_ID (height); + DECLARE_ID (background); DECLARE_ID (interval); DECLARE_ID (textBoxPos); DECLARE_ID (textBoxWidth); @@ -102,6 +106,34 @@ namespace Ids DECLARE_ID (connectedRight); DECLARE_ID (connectedTop); DECLARE_ID (connectedBottom); + DECLARE_ID (juceFolder); + DECLARE_ID (targetFolder); + DECLARE_ID (vstFolder); + DECLARE_ID (rtasFolder); + DECLARE_ID (auFolder); + DECLARE_ID (extraCompilerFlags); + DECLARE_ID (extraLinkerFlags); + DECLARE_ID (extraDefs); + DECLARE_ID (libraryName_Debug); + DECLARE_ID (libraryName_Release); + DECLARE_ID (libraryType); + DECLARE_ID (isDebug); + DECLARE_ID (targetName); + DECLARE_ID (binaryPath); + DECLARE_ID (optimisation); + DECLARE_ID (defines); + DECLARE_ID (headerPath); + DECLARE_ID (osxSDK); + DECLARE_ID (osxCompatibility); + DECLARE_ID (jucerVersion); + DECLARE_ID (projectType); + DECLARE_ID (juceLinkage); + DECLARE_ID (buildVST); + DECLARE_ID (bundleIdentifier); + DECLARE_ID (compile); + DECLARE_ID (resource); + DECLARE_ID (className); + DECLARE_ID (classDesc); const Identifier class_ ("class"); const Identifier id_ ("id"); } diff --git a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.cpp b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.cpp index b4027bf622..576677b97b 100644 --- a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.cpp +++ b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.cpp @@ -76,7 +76,7 @@ public: { RectangleCoordinates r (sourceValue.toString()); Coordinate& coord = getCoord (r); - coord = Coordinate (newValue.toString(), coord.isHorizontal()); + coord = Coordinate (newValue.toString(), type == left || type == right); const String newVal (r.toString()); if (sourceValue != newVal) @@ -123,12 +123,12 @@ public: Coordinate coord (getCoordinate()); PopupMenu m; - document.addComponentMarkerMenuItems (compState, getTypeName(), coord, m, isAnchor1); + document.addComponentMarkerMenuItems (compState, getTypeName(), coord, m, isAnchor1, type == left || type == right); const int r = m.showAt (button); if (r > 0) - return document.getChosenMarkerMenuItem (compState, coord, r); + return document.getChosenMarkerMenuItem (compState, coord, r, type == left || type == right); return String::empty; } @@ -142,10 +142,10 @@ private: { switch (type) { - case left: return "left"; - case right: return "right"; - case top: return "top"; - case bottom: return "bottom"; + case left: return Coordinate::Strings::left; + case right: return Coordinate::Strings::right; + case top: return Coordinate::Strings::top; + case bottom: return Coordinate::Strings::bottom; default: jassertfalse; break; } @@ -181,7 +181,7 @@ Component* ComponentTypeManager::createFromStoredType (ComponentDocument& docume return c; } -ComponentTypeHandler* ComponentTypeManager::getHandlerFor (const String& type) +ComponentTypeHandler* ComponentTypeManager::getHandlerFor (const Identifier& type) { for (int i = handlers.size(); --i >= 0;) if (handlers.getUnchecked(i)->getXmlTag() == type) @@ -248,7 +248,7 @@ public: private: Value sourceValue; - ComponentTypeInstance& item; + ComponentTypeInstance item; CompMemberNameValueSource (const CompMemberNameValueSource&); const CompMemberNameValueSource& operator= (const CompMemberNameValueSource&); diff --git a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.h b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.h index 938c903ff3..8f8dc05973 100644 --- a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.h +++ b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_ComponentTypeManager.h @@ -130,7 +130,7 @@ public: int getNumHandlers() const { return handlers.size(); } ComponentTypeHandler* getHandler (const int index) const { return handlers[index]; } - ComponentTypeHandler* getHandlerFor (const String& type); + ComponentTypeHandler* getHandlerFor (const Identifier& type); const StringArray getDisplayNames() const; private: diff --git a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Label.h b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Label.h index e845c7c327..7d44ddf00e 100644 --- a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Label.h +++ b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Label.h @@ -71,7 +71,7 @@ public: props.add (new TextPropertyComponent (item.getValue (Ids::text), "Text", 16384, true)); props.getLast()->setTooltip ("The label's text."); - item.addJustificationProperty (props, "Layout", item.getValue ("justification"), false); + item.addJustificationProperty (props, "Layout", item.getValue (Ids::justification), false); const char* const editModes[] = { "Read-only", "Edit on Single-Click", "Edit on Double-Click", 0 }; const int values[] = { 1, 2, 3, 0 }; @@ -93,7 +93,7 @@ public: << item.getMemberName() << "->setEditable (" << CodeHelpers::boolLiteral (editMode == 2) << ", " << CodeHelpers::boolLiteral (editMode == 3) << ", false);" << newLine; - Justification justification ((int) item ["textJustification"]); + Justification justification ((int) item [Ids::justification]); if (justification.getFlags() != 0) code.constructorCode << item.getMemberName() << "->setJustificationType (" << CodeHelpers::justificationToCode (justification) << ");" << newLine; diff --git a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Slider.h b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Slider.h index 2b51f32b38..0c2b6b4ea7 100644 --- a/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Slider.h +++ b/extras/Jucer (experimental)/Source/model/Component/Types/jucer_Slider.h @@ -96,8 +96,8 @@ public: props.add (new ChoicePropertyComponent (item.getValue (Ids::textBoxPos), "Text Box", StringArray (textBoxPositions), Array (positionValues, numElementsInArray (positionValues)))); props.add (new BooleanPropertyComponent (item.getValue (Ids::editable), "Editable", "Value can be edited")); - props.add (new TextPropertyComponent (Value (new NumericValueSource (item.getValue ("textBoxWidth"))), "Text Box Width", 8, false)); - props.add (new TextPropertyComponent (Value (new NumericValueSource (item.getValue ("textBoxHeight"))), "Text Box Height", 8, false)); + props.add (new TextPropertyComponent (Value (new NumericValueSource (item.getValue (Ids::textBoxWidth))), "Text Box Width", 8, false)); + props.add (new TextPropertyComponent (Value (new NumericValueSource (item.getValue (Ids::textBoxHeight))), "Text Box Height", 8, false)); props.add (new TextPropertyComponent (Value (new NumericValueSource (item.getValue (Ids::skew))), "Skew Factor", 16, false)); addEditableColourProperties (item, props); diff --git a/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp b/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp index 066ae29ecb..934b5063e9 100644 --- a/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp @@ -311,30 +311,30 @@ void ComponentDocument::checkRootObject() if ((int) getCanvasHeight().getValue() <= 0) getCanvasHeight() = 480; - if (! root.hasProperty ("background")) + if (! root.hasProperty (Ids::background)) getBackgroundColour() = Colours::white.toString(); } void ComponentDocument::setUsingTemporaryCanvasSize (bool b) { - tempCanvasWidth = root.getProperty ("width"); - tempCanvasHeight = root.getProperty ("height"); + tempCanvasWidth = root.getProperty (Ids::width); + tempCanvasHeight = root.getProperty (Ids::height); usingTemporaryCanvasSize = b; } Value ComponentDocument::getCanvasWidth() const { - return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable ("width"); + return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable (Ids::width); } Value ComponentDocument::getCanvasHeight() const { - return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable ("height"); + return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable (Ids::height); } Value ComponentDocument::getBackgroundColour() const { - return getRootValueUndoable ("background"); + return getRootValueUndoable (Ids::background); } //============================================================================== @@ -459,37 +459,42 @@ Component* ComponentDocument::createComponent (int index) } //============================================================================== -const Coordinate ComponentDocument::findMarker (const String& name, bool isHorizontal) const +const Coordinate ComponentDocument::findNamedCoordinate (const String& objectName, const String& edge) const { - if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) getCanvasWidth().getValue(), isHorizontal); - if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) getCanvasHeight().getValue(), isHorizontal); - - if (name.containsChar ('.')) + if (objectName == Coordinate::Strings::parent) { - const String compName (name.upToFirstOccurrenceOf (".", false, false).trim()); - const String edge (name.fromFirstOccurrenceOf (".", false, false).trim()); + if (edge == Coordinate::Strings::right) return Coordinate ((double) getCanvasWidth().getValue(), true); + if (edge == Coordinate::Strings::bottom) return Coordinate ((double) getCanvasHeight().getValue(), false); + } - if (compName.isNotEmpty() && edge.isNotEmpty()) + if (objectName.isNotEmpty() && edge.isNotEmpty()) + { + const ValueTree comp (getComponentWithMemberName (objectName)); + + if (comp.isValid()) { - const ValueTree comp (getComponentWithMemberName (compName)); + const RectangleCoordinates coords (getCoordsFor (comp)); - if (comp.isValid()) - { - const RectangleCoordinates coords (getCoordsFor (comp)); - - if (edge == "left") return coords.left; - if (edge == "right") return coords.right; - if (edge == "top") return coords.top; - if (edge == "bottom") return coords.bottom; - } + if (edge == Coordinate::Strings::left) return coords.left; + if (edge == Coordinate::Strings::right) return coords.right; + if (edge == Coordinate::Strings::top) return coords.top; + if (edge == Coordinate::Strings::bottom) return coords.bottom; } } - const ValueTree marker (getMarkerList (isHorizontal).getMarkerNamed (name)); - if (marker.isValid()) - return getMarkerList (isHorizontal).getCoordinate (marker); + { + const ValueTree marker (getMarkerListX().getMarkerNamed (objectName)); + if (marker.isValid()) + return getMarkerListX().getCoordinate (marker); + } - return Coordinate (isHorizontal); + { + const ValueTree marker (getMarkerListY().getMarkerNamed (objectName)); + if (marker.isValid()) + return getMarkerListY().getCoordinate (marker); + } + + return Coordinate(); } const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& state) const @@ -535,45 +540,50 @@ void ComponentDocument::renameAnchor (const String& oldName, const String& newNa markersY->renameAnchorInMarkers (oldName, newName); } -void ComponentDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu, +void ComponentDocument::addMarkerMenuItem (int i, const Coordinate& coord, + const String& objectName, const String& edge, PopupMenu& menu, bool isAnchor1, const String& fullCoordName) { - Coordinate requestedCoord (findMarker (name, coord.isHorizontal())); + Coordinate requestedCoord (findNamedCoordinate (objectName, edge)); + + String name (objectName); + if (edge.isNotEmpty()) + name << '.' << edge; menu.addItem (i, name, - ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)), - name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2())); + ! (name == fullCoordName || requestedCoord.references (fullCoordName, *this)), + name == (isAnchor1 ? coord.getAnchorName1() : coord.getAnchorName2())); } void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName, - Coordinate& coord, PopupMenu& menu, bool isAnchor1) + Coordinate& coord, PopupMenu& menu, bool isAnchor1, bool isHorizontal) { const String componentName (componentState [memberNameProperty].toString()); const String fullCoordName (componentName + "." + coordName); - if (coord.isHorizontal()) + if (isHorizontal) { - addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName); - addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (1, coord, Coordinate::Strings::parent, Coordinate::Strings::left, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (2, coord, Coordinate::Strings::parent, Coordinate::Strings::right, menu, isAnchor1, fullCoordName); menu.addSeparator(); - addMarkerMenuItem (3, coord, componentName + ".left", menu, isAnchor1, fullCoordName); - addMarkerMenuItem (4, coord, componentName + ".right", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (3, coord, componentName, Coordinate::Strings::left, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (4, coord, componentName, Coordinate::Strings::right, menu, isAnchor1, fullCoordName); } else { - addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName); - addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (1, coord, Coordinate::Strings::parent, Coordinate::Strings::top, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (2, coord, Coordinate::Strings::parent, Coordinate::Strings::bottom, menu, isAnchor1, fullCoordName); menu.addSeparator(); - addMarkerMenuItem (3, coord, componentName + ".top", menu, isAnchor1, fullCoordName); - addMarkerMenuItem (4, coord, componentName + ".bottom", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (3, coord, componentName, Coordinate::Strings::top, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (4, coord, componentName, Coordinate::Strings::bottom, menu, isAnchor1, fullCoordName); } menu.addSeparator(); - const MarkerList& markerList = getMarkerList (coord.isHorizontal()); + const MarkerList& markerList = getMarkerList (isHorizontal); int i; for (i = 0; i < markerList.size(); ++i) - addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName); + addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), String::empty, menu, isAnchor1, fullCoordName); menu.addSeparator(); for (i = 0; i < getNumComponents(); ++i) @@ -582,30 +592,30 @@ void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentS if (compName != componentName) { - if (coord.isHorizontal()) + if (isHorizontal) { - addMarkerMenuItem (10000 + i * 4, coord, compName + ".left", menu, isAnchor1, fullCoordName); - addMarkerMenuItem (10001 + i * 4, coord, compName + ".right", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (10000 + i * 4, coord, compName, Coordinate::Strings::left, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (10001 + i * 4, coord, compName, Coordinate::Strings::right, menu, isAnchor1, fullCoordName); } else { - addMarkerMenuItem (10002 + i * 4, coord, compName + ".top", menu, isAnchor1, fullCoordName); - addMarkerMenuItem (10003 + i * 4, coord, compName + ".bottom", menu, isAnchor1, fullCoordName); + addMarkerMenuItem (10002 + i * 4, coord, compName, Coordinate::Strings::top, menu, isAnchor1, fullCoordName); + addMarkerMenuItem (10003 + i * 4, coord, compName, Coordinate::Strings::bottom, menu, isAnchor1, fullCoordName); } } } } -const String ComponentDocument::getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int i) const +const String ComponentDocument::getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int i, bool isHorizontal) const { const String componentName (componentState [memberNameProperty].toString()); - if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName; - if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName; - if (i == 3) return componentName + (coord.isHorizontal() ? ".left" : ".top"); - if (i == 4) return componentName + (coord.isHorizontal() ? ".right" : ".bottom"); + if (i == 1) return isHorizontal ? Coordinate::Strings::originX : Coordinate::Strings::originY; + if (i == 2) return isHorizontal ? Coordinate::Strings::extentX : Coordinate::Strings::extentY; + if (i == 3) return componentName + (isHorizontal ? ".left" : ".top"); + if (i == 4) return componentName + (isHorizontal ? ".right" : ".bottom"); - const MarkerList& markerList = getMarkerList (coord.isHorizontal()); + const MarkerList& markerList = getMarkerList (isHorizontal); if (i >= 100 && i < 10000) return markerList.getName (markerList.getMarker (i - 100)); @@ -707,49 +717,50 @@ void ComponentDocument::MarkerList::renameAnchor (const String& oldName, const S document.renameAnchor (oldName, newName); } -const Coordinate ComponentDocument::MarkerList::findMarker (const String& name, bool isHorizontal_) const +const Coordinate ComponentDocument::MarkerList::findNamedCoordinate (const String& objectName, const String& edge) const { - if (isHorizontal_ == isX) + if (objectName == Coordinate::Strings::parent) { - if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) document.getCanvasWidth().getValue(), isX); - if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) document.getCanvasHeight().getValue(), isX); - - const ValueTree marker (document.getMarkerList (isX).getMarkerNamed (name)); - if (marker.isValid()) - return document.getMarkerList (isX).getCoordinate (marker); + if (edge == Coordinate::Strings::right) return Coordinate ((double) document.getCanvasWidth().getValue(), true); + if (edge == Coordinate::Strings::bottom) return Coordinate ((double) document.getCanvasHeight().getValue(), false); } - return Coordinate (isX); + const ValueTree marker (getMarkerNamed (objectName)); + if (marker.isValid()) + return getCoordinate (marker); + + return Coordinate(); } void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1) { const String fullCoordName (getName (markerState)); - if (coord.isHorizontal()) + if (isX) { - document.addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName); - document.addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (1, coord, Coordinate::Strings::parent, Coordinate::Strings::left, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (2, coord, Coordinate::Strings::parent, Coordinate::Strings::right, menu, isAnchor1, fullCoordName); } else { - document.addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName); - document.addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (1, coord, Coordinate::Strings::parent, Coordinate::Strings::top, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (2, coord, Coordinate::Strings::parent, Coordinate::Strings::bottom, menu, isAnchor1, fullCoordName); } menu.addSeparator(); - const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); + const MarkerList& markerList = document.getMarkerList (isX); for (int i = 0; i < markerList.size(); ++i) - document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), + String::empty, menu, isAnchor1, fullCoordName); } const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const Coordinate& coord, int i) const { - if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName; - if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName; + if (i == 1) return isX ? "parent.left" : "parent.top"; + if (i == 2) return isX ? "parent.right" : "parent.bottom"; - const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); + const MarkerList& markerList = document.getMarkerList (isX); if (i >= 100 && i < 10000) return markerList.getName (markerList.getMarker (i - 100)); diff --git a/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.h b/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.h index 4d1ca421d6..4e3993f0d6 100644 --- a/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.h +++ b/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.h @@ -35,7 +35,7 @@ //============================================================================== class ComponentDocument : public ValueTree::Listener, - public Coordinate::MarkerResolver + public Coordinate::NamedCoordinateFinder { public: //============================================================================== @@ -57,8 +57,8 @@ public: //============================================================================== const String getUniqueId() const { return root [idProperty]; } - Value getClassName() const { return getRootValueNonUndoable ("className"); } - Value getClassDescription() const { return getRootValueNonUndoable ("classDesc"); } + Value getClassName() const { return getRootValueNonUndoable (Ids::className); } + Value getClassDescription() const { return getRootValueNonUndoable (Ids::classDesc); } void setUsingTemporaryCanvasSize (bool b); Value getCanvasWidth() const; @@ -84,12 +84,12 @@ public: bool setCoordsFor (ValueTree& componentState, const RectangleCoordinates& newSize); void renameAnchor (const String& oldName, const String& newName); - // for Coordinate::MarkerResolver: - const Coordinate findMarker (const String& name, bool isHorizontal) const; + // for Coordinate::Resolver: + const Coordinate findNamedCoordinate (const String& objectName, const String& edge) const; void addComponentMarkerMenuItems (const ValueTree& componentState, const String& coordName, - Coordinate& coord, PopupMenu& menu, bool isAnchor1); - const String getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int itemId) const; + Coordinate& coord, PopupMenu& menu, bool isAnchor1, bool isHorizontal); + const String getChosenMarkerMenuItem (const ValueTree& componentState, Coordinate& coord, int itemId, bool isHorizontal) const; void addNewComponentMenuItems (PopupMenu& menu) const; const ValueTree performNewComponentMenuItem (int menuResultCode); @@ -105,7 +105,7 @@ public: public: MarkerList (ComponentDocument& document, bool isX); - const Coordinate findMarker (const String& name, bool isHorizontal) const; + const Coordinate findNamedCoordinate (const String& objectName, const String& edge) const; bool createProperties (Array & props, const String& itemId); void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; @@ -181,8 +181,8 @@ private: void checkRootObject(); void createSubTreeIfNotThere (const Identifier& name); - void addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu, - bool isAnchor1, const String& fullCoordName); + void addMarkerMenuItem (int i, const Coordinate& coord, const String& objectName, const String& edge, + PopupMenu& menu, bool isAnchor1, const String& fullCoordName); Value getRootValueUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, getUndoManager()); } Value getRootValueNonUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, 0); } diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp index b84376832a..81b2ebff14 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp @@ -27,15 +27,17 @@ //============================================================================== -static const char* const drawableTag = "DRAWABLE"; -static const char* const markersGroupXTag = "MARKERS_X"; -static const char* const markersGroupYTag = "MARKERS_Y"; - +namespace Tags +{ + const Identifier drawableTag ("DRAWABLE"); + const Identifier markersGroupXTag ("MARKERS_X"); + const Identifier markersGroupYTag ("MARKERS_Y"); +} //============================================================================== DrawableDocument::DrawableDocument (Project* project_) : project (project_), - root (drawableTag), + root (Tags::drawableTag), saveAsXml (true), needsSaving (false) { @@ -166,7 +168,7 @@ bool DrawableDocument::load (InputStream& input) loadedTree = ValueTree::readFromStream (input); } - if (loadedTree.hasType (drawableTag)) + if (loadedTree.hasType (Tags::drawableTag)) { addMissingIds (loadedTree); @@ -264,58 +266,63 @@ void DrawableDocument::valueTreeParentChanged (ValueTree& tree) } //============================================================================== -const Coordinate DrawableDocument::findMarker (const String& name, bool isHorizontal) const +const Coordinate DrawableDocument::findNamedCoordinate (const String& objectName, const String& edge) const { - if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) getCanvasWidth().getValue(), isHorizontal); - if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) getCanvasHeight().getValue(), isHorizontal); - - if (name.containsChar ('.')) + if (objectName == "parent") { - const String compName (name.upToFirstOccurrenceOf (".", false, false).trim()); - const String edge (name.fromFirstOccurrenceOf (".", false, false).trim()); - - if (compName.isNotEmpty() && edge.isNotEmpty()) - { -/* const ValueTree comp (getComponentWithMemberName (compName)); - - if (comp.isValid()) - { - const RectangleCoordinates coords (getCoordsFor (comp)); - - if (edge == "left") return coords.left; - if (edge == "right") return coords.right; - if (edge == "top") return coords.top; - if (edge == "bottom") return coords.bottom; - }*/ - } + if (edge == "right") return Coordinate ((double) getCanvasWidth().getValue(), true); + if (edge == "bottom") return Coordinate ((double) getCanvasHeight().getValue(), false); } - const ValueTree marker (getMarkerList (isHorizontal).getMarkerNamed (name)); - if (marker.isValid()) - return getMarkerList (isHorizontal).getCoordinate (marker); + if (objectName.isNotEmpty() && edge.isNotEmpty()) + { +/* const ValueTree comp (getComponentWithMemberName (compName)); - return Coordinate (isHorizontal); + if (comp.isValid()) + { + const RectangleCoordinates coords (getCoordsFor (comp)); + + if (edge == Coordinate::leftName) return coords.left; + if (edge == "right") return coords.right; + if (edge == "top") return coords.top; + if (edge == "bottom") return coords.bottom; + }*/ + } + + { + const ValueTree marker (getMarkerListX().getMarkerNamed (objectName)); + if (marker.isValid()) + return getMarkerListX().getCoordinate (marker); + } + + { + const ValueTree marker (getMarkerListY().getMarkerNamed (objectName)); + if (marker.isValid()) + return getMarkerListY().getCoordinate (marker); + } + + return Coordinate(); } DrawableDocument::MarkerList::MarkerList (DrawableDocument& document_, bool isX_) - : MarkerListBase (document_.getRoot().getChildWithName (isX_ ? markersGroupXTag : markersGroupYTag), isX_), + : MarkerListBase (document_.getRoot().getChildWithName (isX_ ? Tags::markersGroupXTag : Tags::markersGroupYTag), isX_), document (document_) { } -const Coordinate DrawableDocument::MarkerList::findMarker (const String& name, bool isHorizontal_) const +const Coordinate DrawableDocument::MarkerList::findNamedCoordinate (const String& objectName, const String& edge) const { - if (isHorizontal_ == isX) + if (objectName == "parent") { - if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) document.getCanvasWidth().getValue(), isX); - if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) document.getCanvasHeight().getValue(), isX); - - const ValueTree marker (document.getMarkerList (isX).getMarkerNamed (name)); - if (marker.isValid()) - return document.getMarkerList (isX).getCoordinate (marker); + if (edge == "right") return Coordinate ((double) document.getCanvasWidth().getValue(), true); + if (edge == "bottom") return Coordinate ((double) document.getCanvasHeight().getValue(), false); } - return Coordinate (isX); + const ValueTree marker (getMarkerNamed (objectName)); + if (marker.isValid()) + return getCoordinate (marker); + + return Coordinate(); } bool DrawableDocument::MarkerList::createProperties (Array & props, const String& itemId) @@ -334,10 +341,10 @@ bool DrawableDocument::MarkerList::createProperties (Array & return false; } -void DrawableDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu, +void DrawableDocument::addMarkerMenuItem (int i, const Coordinate& coord, const String& objectName, const String& edge, PopupMenu& menu, bool isAnchor1, const String& fullCoordName) { - Coordinate requestedCoord (findMarker (name, coord.isHorizontal())); +// Coordinate requestedCoord (findNamedCoordinate (objectName, edge, coord.isHorizontal())); // menu.addItem (i, name, // ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)), @@ -346,37 +353,38 @@ void DrawableDocument::addMarkerMenuItem (int i, const Coordinate& coord, const void DrawableDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1) { - const String fullCoordName (getName (markerState)); +/* const String fullCoordName (getName (markerState)); if (coord.isHorizontal()) { - document.addMarkerMenuItem (1, coord, Coordinate::parentLeftMarkerName, menu, isAnchor1, fullCoordName); - document.addMarkerMenuItem (2, coord, Coordinate::parentRightMarkerName, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (1, coord, "parent", "left", menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (2, coord, "parent", "right", menu, isAnchor1, fullCoordName); } else { - document.addMarkerMenuItem (1, coord, Coordinate::parentTopMarkerName, menu, isAnchor1, fullCoordName); - document.addMarkerMenuItem (2, coord, Coordinate::parentBottomMarkerName, menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (1, coord, "parent", "top", menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (2, coord, "parent", "bottom", menu, isAnchor1, fullCoordName); } menu.addSeparator(); const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); for (int i = 0; i < markerList.size(); ++i) - document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), menu, isAnchor1, fullCoordName); + document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), + String::empty, menu, isAnchor1, fullCoordName);*/ } const String DrawableDocument::MarkerList::getChosenMarkerMenuItem (const Coordinate& coord, int i) const { - if (i == 1) return coord.isHorizontal() ? Coordinate::parentLeftMarkerName : Coordinate::parentTopMarkerName; - if (i == 2) return coord.isHorizontal() ? Coordinate::parentRightMarkerName : Coordinate::parentBottomMarkerName; +/* if (i == 1) return coord.isHorizontal() ? "parent.left" : "parent.top"; + if (i == 2) return coord.isHorizontal() ? "parent.right" : "parent.bottom"; const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); if (i >= 100 && i < 10000) return markerList.getName (markerList.getMarker (i - 100)); - jassertfalse; + jassertfalse;*/ return String::empty; } diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h index 957d6bf453..ea1272560b 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h @@ -57,8 +57,8 @@ public: void addCircle(); void addImage (const File& imageFile); - Value getCanvasWidth() const { return getRootValueNonUndoable ("width"); } - Value getCanvasHeight() const { return getRootValueNonUndoable ("height"); } + Value getCanvasWidth() const { return getRootValueNonUndoable (Ids::width); } + Value getCanvasHeight() const { return getRootValueNonUndoable (Ids::height); } static const String getIdFor (const ValueTree& object); @@ -69,7 +69,7 @@ public: MarkerList (DrawableDocument& document, bool isX); ~MarkerList() {} - const Coordinate findMarker (const String& name, bool isHorizontal) const; + const Coordinate findNamedCoordinate (const String& objectName, const String& edge) const; bool createProperties (Array & props, const String& itemId); void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; @@ -117,9 +117,9 @@ private: void addMissingIds (ValueTree tree) const; void addDrawable (Drawable& d); - const Coordinate findMarker (const String& name, bool isHorizontal) const; - void addMarkerMenuItem (int i, const Coordinate& coord, const String& name, PopupMenu& menu, - bool isAnchor1, const String& fullCoordName); + const Coordinate findNamedCoordinate (const String& objectName, const String& edge) const; + void addMarkerMenuItem (int i, const Coordinate& coord, const String& objectName, const String& edge, + PopupMenu& menu, bool isAnchor1, const String& fullCoordName); }; diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp index eb25c6dd73..0f8d796316 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp @@ -40,6 +40,7 @@ namespace Tags const Identifier configurations ("CONFIGURATIONS"); const Identifier configuration ("CONFIGURATION"); const Identifier exporters ("EXPORTFORMATS"); + const Identifier configGroup ("JUCEOPTIONS"); } const char* Project::projectFileExtension = ".jucer"; @@ -80,7 +81,7 @@ const String Project::getDocumentTitle() void Project::updateProjectSettings() { - projectRoot.setProperty ("jucerVersion", ProjectInfo::versionString, 0); + projectRoot.setProperty (Ids::jucerVersion, ProjectInfo::versionString, 0); projectRoot.setProperty (Ids::name, getDocumentTitle(), 0); } @@ -101,13 +102,13 @@ void Project::setMissingDefaultValues() if (getDocumentTitle().isEmpty()) setTitle ("Juce Project"); - if (! projectRoot.hasProperty ("projectType")) + if (! projectRoot.hasProperty (Ids::projectType)) getProjectType() = (int) application; - if (! projectRoot.hasProperty ("version")) + if (! projectRoot.hasProperty (Ids::version)) getVersion() = "1.0.0"; - if (! projectRoot.hasProperty ("juceLinkage")) + if (! projectRoot.hasProperty (Ids::juceLinkage)) getJuceLinkageModeValue() = useAmalgamatedJuceViaMultipleTemplates; const String juceFolderPath (getRelativePathForFile (StoredSettings::getInstance()->getLastKnownJuceFolder())); @@ -124,7 +125,7 @@ void Project::setMissingDefaultValues() const String sanitisedProjectName (CodeHelpers::makeValidIdentifier (getProjectName().toString(), false, true, false)); - if (! projectRoot.hasProperty ("buildVST")) + if (! projectRoot.hasProperty (Ids::buildVST)) { shouldBuildVST() = true; shouldBuildRTAS() = false; @@ -147,7 +148,7 @@ void Project::setMissingDefaultValues() getPluginRTASCategory() = String::empty; } - if (! projectRoot.hasProperty ("bundleIdentifier")) + if (! projectRoot.hasProperty (Ids::bundleIdentifier)) setBundleIdentifierToDefault(); } @@ -473,7 +474,7 @@ bool Project::Item::shouldBeCompiled() const Value Project::Item::getShouldCompileValue() const { - return node.getPropertyAsValue ("compile", getUndoManager()); + return node.getPropertyAsValue (Ids::compile, getUndoManager()); } bool Project::Item::shouldBeAddedToBinaryResources() const @@ -483,7 +484,7 @@ bool Project::Item::shouldBeAddedToBinaryResources() const Value Project::Item::getShouldAddToResourceValue() const { - return node.getPropertyAsValue ("resource", getUndoManager()); + return node.getPropertyAsValue (Ids::resource, getUndoManager()); } const File Project::Item::getFile() const @@ -673,11 +674,11 @@ Image* Project::Item::getIcon() const //============================================================================== ValueTree Project::getJuceConfigNode() { - ValueTree configNode = projectRoot.getChildWithName ("JUCEOPTIONS"); + ValueTree configNode = projectRoot.getChildWithName (Tags::configGroup); if (! configNode.isValid()) { - configNode = ValueTree ("JUCEOPTIONS"); + configNode = ValueTree (Tags::configGroup); projectRoot.addChild (configNode, -1, 0); } diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h index c8d84a0d92..37b4da5f3d 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h @@ -221,24 +221,24 @@ public: void createPropertyEditors (Array & properties); //============================================================================== - Value getName() const { return getValue ("name"); } - Value isDebug() const { return getValue ("isDebug"); } - Value getTargetBinaryName() const { return getValue ("targetName"); } + Value getName() const { return getValue (Ids::name); } + Value isDebug() const { return getValue (Ids::isDebug); } + Value getTargetBinaryName() const { return getValue (Ids::targetName); } // the path relative to the build folder in which the binary should go - Value getTargetBinaryRelativePath() const { return getValue ("binaryPath"); } - Value getOptimisationLevel() const { return getValue ("optimisation"); } + Value getTargetBinaryRelativePath() const { return getValue (Ids::binaryPath); } + Value getOptimisationLevel() const { return getValue (Ids::optimisation); } const String getGCCOptimisationFlag() const; - Value getPreprocessorDefs() const { return getValue ("defines"); } + Value getPreprocessorDefs() const { return getValue (Ids::defines); } const StringArray parsePreprocessorDefs() const; - Value getHeaderSearchPath() const { return getValue ("headerPath"); } + Value getHeaderSearchPath() const { return getValue (Ids::headerPath); } const StringArray getHeaderSearchPaths() const; static const char* const osxVersionDefault; static const char* const osxVersion10_4; static const char* const osxVersion10_5; static const char* const osxVersion10_6; - Value getMacSDKVersion() const { return getValue ("osxSDK"); } - Value getMacCompatibilityVersion() const { return getValue ("osxCompatibility"); } + Value getMacSDKVersion() const { return getValue (Ids::osxSDK); } + Value getMacCompatibilityVersion() const { return getValue (Ids::osxCompatibility); } //============================================================================== private: @@ -246,7 +246,7 @@ public: Project* project; ValueTree config; - Value getValue (const char* name) const { return config.getPropertyAsValue (name, getUndoManager()); } + Value getValue (const Identifier& name) const { return config.getPropertyAsValue (name, getUndoManager()); } UndoManager* getUndoManager() const { return project->getUndoManagerFor (config); } BuildConfiguration (Project* project, const ValueTree& configNode); diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h index e41cfe31b4..7ab37f0f8f 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_MSVC.h @@ -128,10 +128,10 @@ public: const int libTypeValues[] = { 1, 2, 0 }; props.add (new ChoicePropertyComponent (getLibraryType(), "Library Type", StringArray (libTypes), Array (libTypeValues))); - props.add (new TextPropertyComponent (getSetting ("libraryName_Debug"), "Library Name (Debug)", 128, false)); + props.add (new TextPropertyComponent (getSetting (Ids::libraryName_Debug), "Library Name (Debug)", 128, false)); props.getLast()->setTooltip ("If set, this name will override the binary name specified in the configuration settings, for a debug build. You must include the .lib or .dll suffix on this filename."); - props.add (new TextPropertyComponent (getSetting ("libraryName_Release"), "Library Name (Release)", 128, false)); + props.add (new TextPropertyComponent (getSetting (Ids::libraryName_Release), "Library Name (Release)", 128, false)); props.getLast()->setTooltip ("If set, this name will override the binary name specified in the configuration settings, for a release build. You must include the .lib or .dll suffix on this filename."); } } @@ -195,7 +195,7 @@ private: const File getDSPFile() const { return getProjectFile (".dsp"); } const File getDSWFile() const { return getProjectFile (".dsw"); } - Value getLibraryType() const { return getSetting ("libraryType"); } + Value getLibraryType() const { return getSetting (Ids::libraryType); } bool isLibraryDLL() const { return project.isLibrary() && getLibraryType() == 2; } //============================================================================== @@ -438,7 +438,7 @@ private: const String getBinaryFileForConfig (const Project::BuildConfiguration& config) const { - const String targetBinary (getSetting (config.isDebug().getValue() ? "libraryName_Debug" : "libraryName_Release").toString().trim()); + const String targetBinary (getSetting (config.isDebug().getValue() ? Ids::libraryName_Debug : Ids::libraryName_Release).toString().trim()); if (targetBinary.isNotEmpty()) return targetBinary; diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_XCode.h b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_XCode.h index de9bd48c49..4162d6603e 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_XCode.h +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExport_XCode.h @@ -570,7 +570,7 @@ private: for (int i = 0; i < objects.size(); ++i) { ValueTree& o = *objects.getUnchecked(i); - output << "\t\t" << o.getType() << " = { "; + output << "\t\t" << static_cast (o.getType()) << " = { "; for (int j = 0; j < o.getNumProperties(); ++j) { @@ -814,7 +814,7 @@ private: StringArray configIDs; for (int i = 0; i < configsToUse.size(); ++i) - configIDs.add (configsToUse[i]->getType()); + configIDs.add (configsToUse[i]->getType().toString()); ValueTree* v = new ValueTree (listID); v->setProperty ("isa", "XCConfigurationList", 0); diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExporter.h b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExporter.h index 0b6b17328c..da8b093113 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExporter.h +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_ProjectExporter.h @@ -61,23 +61,23 @@ public: const File getTargetFolder() const; const ValueTree& getSettings() const { return settings; } - Value getSetting (const Identifier& name_) const { return settings.getPropertyAsValue (name_, project.getUndoManagerFor (settings)); } + Value getSetting (const Identifier& name_) const { return settings.getPropertyAsValue (name_, project.getUndoManagerFor (settings)); } - Value getJuceFolder() const { return getSetting ("juceFolder"); } - Value getTargetLocation() const { return getSetting ("targetFolder"); } + Value getJuceFolder() const { return getSetting (Ids::juceFolder); } + Value getTargetLocation() const { return getSetting (Ids::targetFolder); } - Value getVSTFolder() const { return getSetting ("vstFolder"); } - Value getRTASFolder() const { return getSetting ("rtasFolder"); } - Value getAUFolder() const { return getSetting ("auFolder"); } + Value getVSTFolder() const { return getSetting (Ids::vstFolder); } + Value getRTASFolder() const { return getSetting (Ids::rtasFolder); } + Value getAUFolder() const { return getSetting (Ids::auFolder); } bool isVST() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildVST().getValue(); } bool isRTAS() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildRTAS().getValue(); } bool isAU() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildAU().getValue(); } - Value getExtraCompilerFlags() const { return getSetting ("extraCompilerFlags"); } - Value getExtraLinkerFlags() const { return getSetting ("extraLinkerFlags"); } + Value getExtraCompilerFlags() const { return getSetting (Ids::extraCompilerFlags); } + Value getExtraLinkerFlags() const { return getSetting (Ids::extraLinkerFlags); } - Value getExtraPreprocessorDefs() const { return getSetting ("extraDefs"); } + Value getExtraPreprocessorDefs() const { return getSetting (Ids::extraDefs); } const StringArray parsePreprocessorDefs() const; // This adds the quotes, and may return angle-brackets, eg: or normal quotes. @@ -86,7 +86,7 @@ public: const String getExporterIdentifierMacro() const { return "JUCER_" + settings.getType() + "_" - + String::toHexString (settings ["targetFolder"].toString().hashCode()).toUpperCase(); + + String::toHexString (settings [Ids::targetFolder].toString().hashCode()).toUpperCase(); } Array juceWrapperFiles; diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp index 41fe46ae96..584560219e 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp @@ -91,9 +91,9 @@ public: return editor.getSelection(); } - void getSelectedItemProperties (Array& newComps) + void getSelectedItemProperties (Array& props) { - editor.getSelectedItemProperties (newComps); + editor.getDocument().createItemProperties (props, editor.getSelectedIds()); } private: @@ -178,11 +178,6 @@ const StringArray ComponentEditor::getSelectedIds() const return ids; } -void ComponentEditor::getSelectedItemProperties (Array & props) -{ - getDocument().createItemProperties (props, getSelectedIds()); -} - void ComponentEditor::deleteSelection() { const StringArray ids (getSelectedIds()); diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h index be69bb6621..4047ee1f61 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.h @@ -56,7 +56,6 @@ public: ComponentDocument& getDocument() const { return *componentDocument; } const StringArray getSelectedIds() const; - void getSelectedItemProperties (Array & props); void deleteSelection(); void deselectNonComponents(); void selectionToFront(); 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 8cfa9b4eef..f8b281b3ad 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h @@ -90,9 +90,9 @@ public: return String::empty; } - void showPopupMenu (const Point& position) + void showPopupMenu (bool isClickOnSelectedObject) { - if (findObjectIdAt (position).isNotEmpty()) + if (isClickOnSelectedObject) { PopupMenu m; m.addCommandItem (commandManager, CommandIDs::toFront); diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorToolbar.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorToolbar.h index e8d14cabe0..5b90cc611a 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorToolbar.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorToolbar.h @@ -29,101 +29,6 @@ #include "../../utility/jucer_ColourEditorComponent.h" -//============================================================================== -class JucerToolbarButton : public ToolbarItemComponent -{ -public: - //============================================================================== - JucerToolbarButton (ComponentEditor& editor_, int itemId_, const String& labelText) - : ToolbarItemComponent (itemId_, labelText, true), - editor (editor_) - { - setClickingTogglesState (false); - } - - ~JucerToolbarButton() - { - } - - //============================================================================== - bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize, int& minSize, int& maxSize) - { - preferredSize = minSize = maxSize = 50; - return true; - } - - void paintButton (Graphics& g, bool over, bool down) - { - Path p; - p.addRoundedRectangle (1.5f, 2.5f, getWidth() - 3.0f, getHeight() - 5.0f, 3.0f); - - if (getToggleState()) - { - g.setColour (Colours::grey.withAlpha (0.5f)); - g.fillPath (p); - } - - g.setColour (Colours::darkgrey.withAlpha (0.3f)); - g.strokePath (p, PathStrokeType (1.0f)); - - g.setFont (11.0f); - g.setColour (Colours::black.withAlpha ((over || down) ? 1.0f : 0.7f)); - g.drawFittedText (getButtonText(), 2, 2, getWidth() - 4, getHeight() - 4, Justification::centred, 2); - } - - void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown) - { - } - - void contentAreaChanged (const Rectangle& newBounds) - { - } - - juce_UseDebuggingNewOperator - -protected: - ComponentEditor& editor; - -private: - JucerToolbarButton (const JucerToolbarButton&); - JucerToolbarButton& operator= (const JucerToolbarButton&); -}; - -//============================================================================== -class NewComponentToolbarButton : public JucerToolbarButton -{ -public: - NewComponentToolbarButton (ComponentEditor& editor_, int itemId_) - : JucerToolbarButton (editor_, itemId_, "create...") - { - setTriggeredOnMouseDown (true); - } - - void clicked() - { - editor.showNewComponentMenu (this); - } -}; - - -//============================================================================== -class BackgroundColourToolbarButton : public JucerToolbarButton -{ -public: - BackgroundColourToolbarButton (ComponentEditor& editor_, int itemId_) - : JucerToolbarButton (editor_, itemId_, "background") - { - setTriggeredOnMouseDown (true); - } - - void clicked() - { - editor.getDocument().getUndoManager()->beginNewTransaction(); - PopupColourSelector::showAt (this, editor.getDocument().getBackgroundColour(), Colours::white, true); - } -}; - - //============================================================================== class ComponentEditorToolbarFactory : public ToolbarItemFactory { @@ -138,12 +43,12 @@ public: } //============================================================================== - enum DemoToolbarItemIds + enum ToolbarItemIds { createComponent = 1, changeBackground, showInfo, - showComponentTree, + showTree, showOrHideMarkers, toggleSnapping }; @@ -153,7 +58,7 @@ public: ids.add (createComponent); ids.add (changeBackground); ids.add (showInfo); - ids.add (showComponentTree); + ids.add (showTree); ids.add (showOrHideMarkers); ids.add (toggleSnapping); @@ -171,7 +76,7 @@ public: ids.add (showOrHideMarkers); ids.add (toggleSnapping); ids.add (flexibleSpacerId); - ids.add (showComponentTree); + ids.add (showTree); ids.add (showInfo); ids.add (spacerId); } @@ -186,17 +91,56 @@ public: case createComponent: return new NewComponentToolbarButton (editor, itemId); case changeBackground: return new BackgroundColourToolbarButton (editor, itemId); case showInfo: name = "info"; commandId = CommandIDs::showOrHideProperties; break; - case showComponentTree: name = "tree"; commandId = CommandIDs::showOrHideTree; break; + case showTree: name = "tree"; commandId = CommandIDs::showOrHideTree; break; case showOrHideMarkers: name = "markers"; commandId = CommandIDs::showOrHideMarkers; break; case toggleSnapping: name = "snap"; commandId = CommandIDs::toggleSnapping; break; default: jassertfalse; return 0; } - JucerToolbarButton* b = new JucerToolbarButton (editor, itemId, name); + JucerToolbarButton* b = new JucerToolbarButton (itemId, name); b->setCommandToTrigger (commandManager, commandId, true); return b; } + //============================================================================== + class NewComponentToolbarButton : public JucerToolbarButton + { + public: + NewComponentToolbarButton (ComponentEditor& editor_, int itemId_) + : JucerToolbarButton (itemId_, "create..."), editor (editor_) + { + setTriggeredOnMouseDown (true); + } + + void clicked() + { + editor.showNewComponentMenu (this); + } + + private: + ComponentEditor& editor; + }; + + //============================================================================== + class BackgroundColourToolbarButton : public JucerToolbarButton + { + public: + BackgroundColourToolbarButton (ComponentEditor& editor_, int itemId_) + : JucerToolbarButton (itemId_, "background"), editor (editor_) + { + setTriggeredOnMouseDown (true); + } + + void clicked() + { + editor.getDocument().getUndoManager()->beginNewTransaction(); + PopupColourSelector::showAt (this, editor.getDocument().getBackgroundColour(), Colours::white, true); + } + + private: + ComponentEditor& editor; + }; + private: ComponentEditor& editor; diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentViewer.cpp b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentViewer.cpp index d1dea9a913..150e929fa0 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentViewer.cpp +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentViewer.cpp @@ -107,7 +107,8 @@ void ComponentViewer::handleAsyncUpdate() componentsInOrder.add (c); - layoutManager->setComponentLayout (c, v [ComponentDocument::memberNameProperty], componentDocument->getCoordsFor (v)); + layoutManager->setComponentBounds (c, v [ComponentDocument::memberNameProperty], + componentDocument->getCoordsFor (v)); } // Make sure the z-order is correct.. diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp index ccc6e52b59..185b283e4c 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp @@ -53,7 +53,7 @@ public: void createCanvas() { initialise (new DrawableEditorCanvas (editor), toolbarFactory, - DrawableTreeViewItem::createItemForNode (editor, editor.getDocument().getRootDrawableNode())); + new DrawableTreeViewItem (editor, editor.getDocument().getRootDrawableNode())); } SelectedItemSet& getSelection() @@ -101,3 +101,155 @@ void DrawableEditor::resized() { panel->setBounds (getLocalBounds()); } + +//============================================================================== +void DrawableEditor::deleteSelection() +{ +} + +void DrawableEditor::selectionToFront() +{ +} + +void DrawableEditor::selectionToBack() +{ +} + +void DrawableEditor::showNewShapeMenu (Component* componentToAttachTo) +{ + /* + PopupMenu m; + getDocument().addNewComponentMenuItems (m); + + const int r = m.showAt (componentToAttachTo); + + const ValueTree newComp (getDocument().performNewComponentMenuItem (r)); + + if (newComp.isValid()) + getSelection().selectOnly (newComp [ComponentDocument::idProperty]); +*/ +} + +//============================================================================== +void DrawableEditor::getAllCommands (Array & commands) +{ + DocumentEditorComponent::getAllCommands (commands); + + const CommandID ids[] = { CommandIDs::undo, + CommandIDs::redo, + CommandIDs::toFront, + CommandIDs::toBack, + CommandIDs::showOrHideProperties, + CommandIDs::showOrHideTree, + CommandIDs::showOrHideMarkers, + CommandIDs::toggleSnapping, + StandardApplicationCommandIDs::del }; + + commands.addArray (ids, numElementsInArray (ids)); +} + +void DrawableEditor::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) +{ + result.setActive (document != 0); + + switch (commandID) + { + case CommandIDs::undo: + result.setInfo ("Undo", "Undoes the last change", CommandCategories::general, 0); + result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier, 0)); + break; + + case CommandIDs::redo: + result.setInfo ("Redo", "Redoes the last change", CommandCategories::general, 0); + result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0)); + result.defaultKeypresses.add (KeyPress ('y', ModifierKeys::commandModifier, 0)); + break; + + case CommandIDs::toFront: + result.setInfo ("Bring to Front", "Brings the selected items to the front", CommandCategories::editing, 0); + break; + + case CommandIDs::toBack: + result.setInfo ("Send to Back", "Moves the selected items to the back", CommandCategories::editing, 0); + break; + + case CommandIDs::showOrHideProperties: + result.setInfo ("Show/Hide Tree", "Shows or hides the component tree view", CommandCategories::editing, 0); + result.setTicked (panel != 0 && panel->arePropertiesVisible()); + break; + + case CommandIDs::showOrHideTree: + result.setInfo ("Show/Hide Properties", "Shows or hides the component properties panel", CommandCategories::editing, 0); + result.setTicked (panel != 0 && panel->isTreeVisible()); + break; + + case CommandIDs::showOrHideMarkers: + result.setInfo ("Show/Hide Markers", "Shows or hides the markers", CommandCategories::editing, 0); + result.setTicked (panel != 0 && panel->areMarkersVisible()); + break; + + case CommandIDs::toggleSnapping: + result.setInfo ("Toggle snapping", "Turns object snapping on or off", CommandCategories::editing, 0); + result.setTicked (panel != 0 && panel->isSnappingEnabled()); + break; + + case StandardApplicationCommandIDs::del: + result.setInfo ("Delete", String::empty, CommandCategories::general, 0); + result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0)); + result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0)); + break; + + default: + DocumentEditorComponent::getCommandInfo (commandID, result); + break; + } +} + +bool DrawableEditor::perform (const InvocationInfo& info) +{ + switch (info.commandID) + { + case CommandIDs::undo: + getDocument().getUndoManager()->beginNewTransaction(); + getDocument().getUndoManager()->undo(); + return true; + + case CommandIDs::redo: + getDocument().getUndoManager()->beginNewTransaction(); + getDocument().getUndoManager()->redo(); + return true; + + case CommandIDs::toFront: + selectionToFront(); + return true; + + case CommandIDs::toBack: + selectionToBack(); + return true; + + case CommandIDs::showOrHideProperties: + panel->showOrHideProperties(); + return true; + + case CommandIDs::showOrHideTree: + panel->showOrHideTree(); + return true; + + case CommandIDs::showOrHideMarkers: + panel->showOrHideMarkers(); + return true; + + case CommandIDs::toggleSnapping: + panel->toggleSnapping(); + return true; + + case StandardApplicationCommandIDs::del: + deleteSelection(); + return true; + + default: + break; + } + + return DocumentEditorComponent::perform (info); +} diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h index afb8c1fbda..06ad22c253 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h @@ -42,9 +42,21 @@ public: Project* project, DrawableDocument* drawableDocument); ~DrawableEditor(); + //============================================================================== + void getAllCommands (Array & commands); + void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); + bool perform (const InvocationInfo& info); + void paint (Graphics& g); void resized(); + //============================================================================== + void deleteSelection(); + void selectionToFront(); + void selectionToBack(); + void showNewShapeMenu (Component* componentToAttachTo); + + //============================================================================== DrawableDocument& getDocument() const { return *drawableDocument; } EditorCanvasBase::SelectedItems& getSelection() { return selection; } 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 1afcc9e436..0e783dcdb9 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h @@ -74,11 +74,11 @@ public: return String::empty; } - void showPopupMenu (const Point& position) + void showPopupMenu (bool isClickOnSelectedObject) { PopupMenu m; - if (findObjectIdAt (position).isNotEmpty()) + if (isClickOnSelectedObject) { m.addCommandItem (commandManager, CommandIDs::toFront); m.addCommandItem (commandManager, CommandIDs::toBack); diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorToolbar.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorToolbar.h index 0cb956c1f7..628f0eae6c 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorToolbar.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorToolbar.h @@ -41,17 +41,22 @@ public: } //============================================================================== - enum DemoToolbarItemIds + enum ToolbarItemIds { - createComponent = 1, - showInfo = 2, - showComponentTree = 3, + createShape = 1, + showInfo, + showTree, + showOrHideMarkers, + toggleSnapping }; void getAllToolbarItemIds (Array & ids) { + ids.add (createShape); ids.add (showInfo); - ids.add (showComponentTree); + ids.add (showTree); + ids.add (showOrHideMarkers); + ids.add (toggleSnapping); ids.add (separatorBarId); ids.add (spacerId); @@ -61,8 +66,12 @@ public: void getDefaultItemSet (Array & ids) { ids.add (spacerId); + ids.add (createShape); ids.add (flexibleSpacerId); - ids.add (showComponentTree); + ids.add (showOrHideMarkers); + ids.add (toggleSnapping); + ids.add (flexibleSpacerId); + ids.add (showTree); ids.add (showInfo); ids.add (spacerId); } @@ -74,16 +83,38 @@ public: switch (itemId) { + case createShape: return new NewShapeToolbarButton (editor, itemId); case showInfo: name = "info"; commandId = CommandIDs::showOrHideProperties; break; - case showComponentTree: name = "tree"; commandId = CommandIDs::showOrHideTree; break; + case showTree: name = "tree"; commandId = CommandIDs::showOrHideTree; break; + case showOrHideMarkers: name = "markers"; commandId = CommandIDs::showOrHideMarkers; break; + case toggleSnapping: name = "snap"; commandId = CommandIDs::toggleSnapping; break; default: jassertfalse; return 0; } - ToolbarButton* b = new ToolbarButton (itemId, name, new DrawablePath(), 0); + JucerToolbarButton* b = new JucerToolbarButton (itemId, name); b->setCommandToTrigger (commandManager, commandId, true); return b; } + //============================================================================== + class NewShapeToolbarButton : public JucerToolbarButton + { + public: + NewShapeToolbarButton (DrawableEditor& editor_, int itemId_) + : JucerToolbarButton (itemId_, "create..."), editor (editor_) + { + setTriggeredOnMouseDown (true); + } + + void clicked() + { + editor.showNewShapeMenu (this); + } + + private: + DrawableEditor& editor; + }; + private: DrawableEditor& editor; diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h index 0096418dec..54d5628e6e 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h @@ -34,44 +34,14 @@ class DrawableTreeViewItem : public JucerTreeViewBase, public ValueTree::Listener, public ChangeListener { - DrawableTreeViewItem (DrawableEditor& editor_, const ValueTree& drawableRoot, const String& typeName_) - : editor (editor_), node (drawableRoot), typeName (typeName_) +public: + DrawableTreeViewItem (DrawableEditor& editor_, const ValueTree& drawableRoot) + : editor (editor_), node (drawableRoot), typeName (drawableRoot.getType().toString()) { node.addListener (this); editor.getSelection().addChangeListener (this); } -public: - static DrawableTreeViewItem* createItemForNode (DrawableEditor& editor, const ValueTree& drawableRoot) - { - const char* typeName = 0; - - { - ScopedPointer d (Drawable::createFromValueTree (drawableRoot)); - - if (d != 0) - { - if (dynamic_cast ((Drawable*) d) != 0) - typeName = "Path"; - else if (dynamic_cast ((Drawable*) d) != 0) - typeName = "Image"; - else if (dynamic_cast ((Drawable*) d) != 0) - typeName = "Group"; - else if (dynamic_cast ((Drawable*) d) != 0) - typeName = "Text"; - else - { - jassertfalse - } - } - } - - if (typeName != 0) - return new DrawableTreeViewItem (editor, drawableRoot, typeName); - - return 0; - } - ~DrawableTreeViewItem() { editor.getSelection().removeChangeListener (this); @@ -97,7 +67,8 @@ public: // TreeViewItem stuff.. bool mightContainSubItems() { - return node.getNumChildren() > 0; + return node.getType() == DrawableComposite::valueTreeType + && node.getNumChildren() > 0; } const String getUniqueName() const @@ -114,23 +85,24 @@ public: void refreshSubItems() { - ScopedPointer oldOpenness (getOpennessState()); - - clearSubItems(); - - for (int i = 0; i < node.getNumChildren(); ++i) + if (node.getType() == DrawableComposite::valueTreeType) { - ValueTree subNode (node.getChild (i)); - DrawableTreeViewItem* const item = createItemForNode (editor, subNode); + ScopedPointer oldOpenness (getOpennessState()); - if (item != 0) + clearSubItems(); + + for (int i = 0; i < node.getNumChildren(); ++i) + { + ValueTree subNode (node.getChild (i)); + DrawableTreeViewItem* const item = new DrawableTreeViewItem (editor, subNode); addSubItem (item); + } + + if (oldOpenness != 0) + restoreOpennessState (*oldOpenness); + + editor.getSelection().changed(); } - - if (oldOpenness != 0) - restoreOpennessState (*oldOpenness); - - editor.getSelection().changed(); } const String getDisplayName() const 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 980084502c..94bb1e6f36 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp @@ -37,7 +37,8 @@ public: : OverlayItemComponent (canvas_), objectState (objectState_), objectId (objectId_), - borderThickness (4) + borderThickness (4), + isDragging (false) { jassert (objectState.isValid()); } @@ -59,26 +60,50 @@ public: void mouseDown (const MouseEvent& e) { updateDragZone (e.getPosition()); - canvas->beginDrag (e.getEventRelativeTo (getParentComponent()), dragZone); - canvas->showSizeGuides(); + + if (e.mods.isPopupMenu()) + { + isDragging = false; + canvas->showPopupMenu (true); + } + else + { + isDragging = true; + canvas->beginDrag (e.getEventRelativeTo (getParentComponent()), dragZone); + canvas->showSizeGuides(); + } } void mouseDrag (const MouseEvent& e) { - canvas->continueDrag (e.getEventRelativeTo (getParentComponent())); - autoScrollForMouseEvent (e); + if (isDragging) + { + canvas->continueDrag (e.getEventRelativeTo (getParentComponent())); + autoScrollForMouseEvent (e); + } } void mouseUp (const MouseEvent& e) { - canvas->hideSizeGuides(); - canvas->endDrag (e.getEventRelativeTo (getParentComponent())); - updateDragZone (e.getPosition()); + if (isDragging) + { + canvas->hideSizeGuides(); + canvas->endDrag (e.getEventRelativeTo (getParentComponent())); + updateDragZone (e.getPosition()); + } + } + + void mouseDoubleClick (const MouseEvent& e) + { + canvas->objectDoubleClicked (e, objectState); } bool hitTest (int x, int y) { - return ! getCentreArea().contains (x, y); + if (ModifierKeys::getCurrentModifiers().isAnyModifierKeyDown()) + return ! getCentreArea().contains (x, y); + + return true; } bool updatePosition() @@ -130,7 +155,7 @@ public: void updatePosition (const Rectangle& bounds) { RectangleCoordinates coords (canvas->getObjectCoords (state)); - Coordinate coord (false); + Coordinate coord; Rectangle r; switch (type) @@ -174,6 +199,7 @@ private: ResizableBorderComponent::Zone dragZone; const int borderThickness; OwnedArray sizeGuides; + bool isDragging; const Rectangle getCentreArea() const { @@ -389,7 +415,7 @@ public: if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse)) getSelection().selectOnly (underMouse); - canvas->showPopupMenu (e2.getPosition()); + canvas->showPopupMenu (underMouse.isNotEmpty()); } else { 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 0b3570da2a..7f792d6870 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h @@ -72,7 +72,7 @@ public: virtual MarkerListBase& getMarkerList (bool isX) = 0; virtual const SelectedItems::ItemType findObjectIdAt (const Point& position) = 0; - virtual void showPopupMenu (const Point& position) = 0; + virtual void showPopupMenu (bool isClickOnSelectedObject) = 0; virtual void objectDoubleClicked (const MouseEvent& e, const ValueTree& state) = 0; virtual const ValueTree getObjectState (const String& objectId) = 0; diff --git a/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.cpp b/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.cpp index 75d6ce95b7..1a37571c15 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.cpp +++ b/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.cpp @@ -27,113 +27,140 @@ //============================================================================== -const char* Coordinate::parentLeftMarkerName = "parent.left"; -const char* Coordinate::parentRightMarkerName = "parent.right"; -const char* Coordinate::parentTopMarkerName = "parent.top"; -const char* Coordinate::parentBottomMarkerName = "parent.bottom"; - -Coordinate::Coordinate (bool horizontal_) - : value (0), isProportion (false), horizontal (horizontal_) +Coordinate::Coordinate() + : value (0) { } -Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool horizontal_) - : value (absoluteDistanceFromOrigin), isProportion (false), horizontal (horizontal_) +Coordinate::Coordinate (const double absoluteDistanceFromOrigin, const bool horizontal_) + : anchor1 (getOriginAnchorName (horizontal_)), + value (absoluteDistanceFromOrigin) { } -Coordinate::Coordinate (double absoluteDistance, const String& source, bool horizontal_) - : anchor1 (source), value (absoluteDistance), isProportion (false), horizontal (horizontal_) +Coordinate::Coordinate (const double absoluteDistance, const String& source) + : anchor1 (source.trim()), + value (absoluteDistance) { + jassert (anchor1.isNotEmpty()); } -Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool horizontal_) - : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), horizontal (horizontal_) +Coordinate::Coordinate (const double relativeProportion, const String& pos1, const String& pos2) + : anchor1 (pos1.trim()), + anchor2 (pos2.trim()), + value (relativeProportion) { + jassert (anchor1.isNotEmpty()); + jassert (anchor2.isNotEmpty()); } Coordinate::~Coordinate() { } -const Coordinate Coordinate::getAnchorPoint1() const +//============================================================================== +const String Coordinate::Strings::parent ("parent"); +const String Coordinate::Strings::left ("left"); +const String Coordinate::Strings::right ("right"); +const String Coordinate::Strings::top ("top"); +const String Coordinate::Strings::bottom ("bottom"); + +const String Coordinate::Strings::originX ("parent.left"); +const String Coordinate::Strings::originY ("parent.top"); +const String Coordinate::Strings::extentX ("parent.right"); +const String Coordinate::Strings::extentY ("parent.bottom"); + +const String Coordinate::getObjectName (const String& fullName) { - return Coordinate (0.0, anchor1, horizontal); + return fullName.upToFirstOccurrenceOf (".", false, false); } -const Coordinate Coordinate::getAnchorPoint2() const +const String Coordinate::getEdgeName (const String& fullName) { - return Coordinate (0.0, anchor2, horizontal); + return fullName.fromFirstOccurrenceOf (".", false, false); +} + +const Coordinate Coordinate::getAnchorCoordinate1() const +{ + return Coordinate (0.0, anchor1); +} + +const Coordinate Coordinate::getAnchorCoordinate2() const +{ + return Coordinate (0.0, anchor2); } bool Coordinate::isOrigin (const String& name) { - return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName; + return name.isEmpty() + || name == Strings::originX + || name == Strings::originY; } -const String Coordinate::getOriginMarkerName() const +const String Coordinate::getOriginAnchorName (const bool isHorizontal) const throw() { - return horizontal ? parentLeftMarkerName : parentTopMarkerName; + return isHorizontal ? Strings::originX : Strings::originY; } -const String Coordinate::getExtentMarkerName() const +const String Coordinate::getExtentAnchorName (const bool isHorizontal) const throw() { - return horizontal ? parentRightMarkerName : parentBottomMarkerName; + return isHorizontal ? Strings::extentX : Strings::extentY; } -const String Coordinate::checkName (const String& name) const +//============================================================================== +Coordinate::RecursiveCoordinateException::RecursiveCoordinateException() + : std::runtime_error ("Coordinate::RecursiveCoordinateException") { - return name.isEmpty() ? getOriginMarkerName() : name; } -double Coordinate::getPosition (const String& name, const MarkerResolver& markerResolver, int recursionCounter) const +const Coordinate Coordinate::lookUpName (const String& name, const NamedCoordinateFinder& nameSource) const { - if (isOrigin (name)) + return nameSource.findNamedCoordinate (getObjectName (name), getEdgeName (name)); +} + +double Coordinate::resolveAnchor (const String& anchorName, const NamedCoordinateFinder& nameSource, int recursionCounter) const +{ + if (isOrigin (anchorName)) return 0.0; - return markerResolver.findMarker (name, horizontal) - .resolve (markerResolver, recursionCounter + 1); + return lookUpName (anchorName, nameSource).resolve (nameSource, recursionCounter + 1); } -struct RecursivePositionException -{ -}; - -double Coordinate::resolve (const MarkerResolver& markerResolver, int recursionCounter) const +double Coordinate::resolve (const NamedCoordinateFinder& nameSource, int recursionCounter) const { if (recursionCounter > 100) { jassertfalse - throw RecursivePositionException(); + throw RecursiveCoordinateException(); } - const double pos1 = getPosition (anchor1, markerResolver, recursionCounter); + const double pos1 = resolveAnchor (anchor1, nameSource, recursionCounter); - return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value - : pos1 + value; + return isProportional() ? pos1 + (resolveAnchor (anchor2, nameSource, recursionCounter) - pos1) * value + : pos1 + value; } -double Coordinate::resolve (const MarkerResolver& markerResolver) const +double Coordinate::resolve (const NamedCoordinateFinder& nameSource) const { try { - return resolve (markerResolver, 0); + return resolve (nameSource, 0); } - catch (RecursivePositionException&) + catch (RecursiveCoordinateException&) {} return 0.0; } -void Coordinate::moveToAbsolute (double newPos, const MarkerResolver& markerResolver) +void Coordinate::moveToAbsolute (double newPos, const NamedCoordinateFinder& nameSource) { try { - const double pos1 = getPosition (anchor1, markerResolver, 0); + const double pos1 = resolveAnchor (anchor1, nameSource, 0); - if (isProportion) + if (isProportional()) { - const double size = getPosition (anchor2, markerResolver, 0) - pos1; + const double size = resolveAnchor (anchor2, nameSource, 0) - pos1; if (size != 0) value = (newPos - pos1) / size; @@ -143,242 +170,295 @@ void Coordinate::moveToAbsolute (double newPos, const MarkerResolver& markerReso value = newPos - pos1; } } - catch (RecursivePositionException&) + catch (RecursiveCoordinateException&) {} } -bool Coordinate::referencesDirectly (const String& markerName) const +void Coordinate::toggleProportionality (const NamedCoordinateFinder& nameSource, bool isHorizontal) { - jassert (markerName.isNotEmpty()); - return anchor1 == markerName || anchor2 == markerName; + const double oldValue = resolve (nameSource); + + anchor1 = getOriginAnchorName (isHorizontal); + anchor2 = isProportional() ? String::empty + : getExtentAnchorName (isHorizontal); + + moveToAbsolute (oldValue, nameSource); } -bool Coordinate::referencesIndirectly (const String& markerName, const MarkerResolver& markerResolver) const +//============================================================================== +bool Coordinate::references (const String& coordName, const NamedCoordinateFinder& nameSource) const { - if (isOrigin (anchor1) && ! isProportion) - return isOrigin (markerName); + if (isOrigin (anchor1) && ! isProportional()) + return isOrigin (coordName); - return referencesDirectly (markerName) - || markerResolver.findMarker (anchor1, horizontal).referencesIndirectly (markerName, markerResolver) - || (isProportion && markerResolver.findMarker (anchor2, horizontal).referencesIndirectly (markerName, markerResolver)); + return anchor1 == coordName + || anchor2 == coordName + || lookUpName (anchor1, nameSource).references (coordName, nameSource) + || (isProportional() && lookUpName (anchor2, nameSource).references (coordName, nameSource)); } -void Coordinate::skipWhitespace (const String& s, int& i) +//============================================================================== +namespace CoordParserHelpers { - while (CharacterFunctions::isWhitespace (s[i])) - ++i; -} - -const String Coordinate::readMarkerName (const String& s, int& i) -{ - skipWhitespace (s, i); - - if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') + static void skipWhitespace (const String& s, int& i) { + while (CharacterFunctions::isWhitespace (s[i])) + ++i; + } + + static const String readAnchorName (const String& s, int& i) + { + skipWhitespace (s, i); + + if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') + { + int start = i; + + while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') + ++i; + + return s.substring (start, i); + } + + return String::empty; + } + + static double readNumber (const String& s, int& i) + { + skipWhitespace (s, i); + int start = i; - while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') + if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') ++i; - return s.substring (start, i); + while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') + ++i; + + if ((s[i] == 'e' || s[i] == 'E') + && (CharacterFunctions::isDigit (s[i + 1]) + || s[i + 1] == '-' + || s[i + 1] == '+')) + { + i += 2; + + while (CharacterFunctions::isDigit (s[i])) + ++i; + } + + const double value = s.substring (start, i).getDoubleValue(); + + while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') + ++i; + + return value; } - return String::empty; -} - -double Coordinate::readNumber (const String& s, int& i) -{ - skipWhitespace (s, i); - - int start = i; - - if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') - ++i; - - while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') - ++i; - - if ((s[i] == 'e' || s[i] == 'E') - && (CharacterFunctions::isDigit (s[i + 1]) - || s[i + 1] == '-' - || s[i + 1] == '+')) + static const String limitedAccuracyString (const double n) { - i += 2; - - while (CharacterFunctions::isDigit (s[i])) - ++i; + return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd ("."); } - - const double value = s.substring (start, i).getDoubleValue(); - - while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') - ++i; - - return value; } -Coordinate::Coordinate (const String& s, bool horizontal_) - : value (0), isProportion (false), horizontal (horizontal_) +Coordinate::Coordinate (const String& s, bool isHorizontal) + : value (0) { int i = 0; - anchor1 = readMarkerName (s, i); + anchor1 = CoordParserHelpers::readAnchorName (s, i); if (anchor1.isNotEmpty()) { - skipWhitespace (s, i); + CoordParserHelpers::skipWhitespace (s, i); if (s[i] == '+') - value = readNumber (s, ++i); + value = CoordParserHelpers::readNumber (s, ++i); else if (s[i] == '-') - value = -readNumber (s, ++i); + value = -CoordParserHelpers::readNumber (s, ++i); } else { - value = readNumber (s, i); - skipWhitespace (s, i); + anchor1 = getOriginAnchorName (isHorizontal); + + value = CoordParserHelpers::readNumber (s, i); + CoordParserHelpers::skipWhitespace (s, i); if (s[i] == '%') { - isProportion = true; value /= 100.0; - skipWhitespace (s, ++i); + CoordParserHelpers::skipWhitespace (s, ++i); if (s[i] == '*') { - anchor1 = readMarkerName (s, ++i); - skipWhitespace (s, i); + anchor1 = CoordParserHelpers::readAnchorName (s, ++i); + + if (anchor1.isEmpty()) + anchor1 = getOriginAnchorName (isHorizontal); + + CoordParserHelpers::skipWhitespace (s, i); if (s[i] == '-' && s[i + 1] == '>') { i += 2; - anchor2 = readMarkerName (s, i); + anchor2 = CoordParserHelpers::readAnchorName (s, i); } else { anchor2 = anchor1; - anchor1 = getOriginMarkerName(); + anchor1 = getOriginAnchorName (isHorizontal); } } else { - anchor1 = getOriginMarkerName(); - anchor2 = getExtentMarkerName(); + anchor1 = getOriginAnchorName (isHorizontal); + anchor2 = getExtentAnchorName (isHorizontal); } } } } -static const String limitedAccuracyString (const double n) -{ - return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd ("."); -} - const String Coordinate::toString() const { - if (isProportion) + if (isProportional()) { - const String percent (limitedAccuracyString (value * 100.0)); + const String percent (CoordParserHelpers::limitedAccuracyString (value * 100.0)); if (isOrigin (anchor1)) { - if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName) + if (anchor2 == "parent.right" || anchor2 == "parent.bottom") return percent + "%"; else - return percent + "% * " + checkName (anchor2); + return percent + "% * " + anchor2; } else - return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2); + return percent + "% * " + anchor1 + " -> " + anchor2; } else { if (isOrigin (anchor1)) - return limitedAccuracyString (value); + return CoordParserHelpers::limitedAccuracyString (value); else if (value > 0) - return checkName (anchor1) + " + " + limitedAccuracyString (value); + return anchor1 + " + " + CoordParserHelpers::limitedAccuracyString (value); else if (value < 0) - return checkName (anchor1) + " - " + limitedAccuracyString (-value); + return anchor1 + " - " + CoordParserHelpers::limitedAccuracyString (-value); else - return checkName (anchor1); + return anchor1; } } -const double Coordinate::getEditableValue() const +//============================================================================== +const double Coordinate::getEditableNumber() const { - return isProportion ? value * 100.0 : value; + return isProportional() ? value * 100.0 : value; } -void Coordinate::setEditableValue (const double newValue) +void Coordinate::setEditableNumber (const double newValue) { - value = isProportion ? newValue / 100.0 : newValue; + value = isProportional() ? newValue / 100.0 : newValue; } -void Coordinate::toggleProportionality (const MarkerResolver& markerResolver) +//============================================================================== +void Coordinate::changeAnchor1 (const String& newAnchorName, const NamedCoordinateFinder& nameSource) { - const double oldValue = resolve (markerResolver); + jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_.")); - isProportion = ! isProportion; - anchor1 = getOriginMarkerName(); - anchor2 = getExtentMarkerName(); - - moveToAbsolute (oldValue, markerResolver); + const double oldValue = resolve (nameSource); + anchor1 = newAnchorName; + moveToAbsolute (oldValue, nameSource); } -void Coordinate::changeAnchor1 (const String& newMarkerName, const MarkerResolver& markerResolver) +void Coordinate::changeAnchor2 (const String& newAnchorName, const NamedCoordinateFinder& nameSource) { - const double oldValue = resolve (markerResolver); - anchor1 = newMarkerName; - moveToAbsolute (oldValue, markerResolver); + jassert (isProportional()); + jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_.")); + + const double oldValue = resolve (nameSource); + anchor2 = newAnchorName; + moveToAbsolute (oldValue, nameSource); } -void Coordinate::changeAnchor2 (const String& newMarkerName, const MarkerResolver& markerResolver) -{ - const double oldValue = resolve (markerResolver); - anchor2 = newMarkerName; - moveToAbsolute (oldValue, markerResolver); -} - -void Coordinate::renameAnchorIfUsed (const String& oldName, const String& newName, const MarkerResolver& markerResolver) +void Coordinate::renameAnchorIfUsed (const String& oldName, const String& newName, const NamedCoordinateFinder& nameSource) { jassert (oldName.isNotEmpty()); + jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); if (newName.isEmpty()) { - if (anchor1.upToFirstOccurrenceOf (".", false, false) == oldName - || anchor2.upToFirstOccurrenceOf (".", false, false) == oldName) + if (getObjectName (anchor1) == oldName + || getObjectName (anchor2) == oldName) { - value = resolve (markerResolver); - isProportion = false; + value = resolve (nameSource); anchor1 = String::empty; anchor2 = String::empty; } } else { - if (anchor1.upToFirstOccurrenceOf (".", false, false) == oldName) - anchor1 = newName + anchor1.fromFirstOccurrenceOf (".", true, false); + if (getObjectName (anchor1) == oldName) + anchor1 = newName + "." + getEdgeName (anchor1); - if (anchor2.upToFirstOccurrenceOf (".", false, false) == oldName) - anchor2 = newName + anchor2.fromFirstOccurrenceOf (".", true, false); + if (getObjectName (anchor2) == oldName) + anchor2 = newName + "." + getEdgeName (anchor2); } } +//============================================================================== +CoordinatePair::CoordinatePair() +{ +} + +CoordinatePair::CoordinatePair (const Point& absolutePoint) + : x (absolutePoint.getX(), true), y (absolutePoint.getY(), false) +{ +} + +CoordinatePair::CoordinatePair (const String& stringVersion) +{ + StringArray tokens; + tokens.addTokens (stringVersion, ",", String::empty); + + x = Coordinate (tokens [0], true); + y = Coordinate (tokens [1], false); +} + +const Point CoordinatePair::resolve (const Coordinate::NamedCoordinateFinder& nameSource) const +{ + return Point ((float) x.resolve (nameSource), + (float) y.resolve (nameSource)); +} + +void CoordinatePair::moveToAbsolute (const Point& newPos, const Coordinate::NamedCoordinateFinder& nameSource) +{ + x.moveToAbsolute (newPos.getX(), nameSource); + y.moveToAbsolute (newPos.getY(), nameSource); +} + +const String CoordinatePair::toString() const +{ + return x.toString() + ", " + y.toString(); +} + +void CoordinatePair::renameAnchorIfUsed (const String& oldName, const String& newName, const Coordinate::NamedCoordinateFinder& nameSource) +{ + x.renameAnchorIfUsed (oldName, newName, nameSource); + y.renameAnchorIfUsed (oldName, newName, nameSource); +} + + //============================================================================== RectangleCoordinates::RectangleCoordinates() - : left (true), right (true), top (false), bottom (false) { } RectangleCoordinates::RectangleCoordinates (const Rectangle& rect, const String& componentName) : left (rect.getX(), true), - right (rect.getWidth(), componentName + ".left", true), + right (rect.getWidth(), componentName + "." + Coordinate::Strings::left), top (rect.getY(), false), - bottom (rect.getHeight(), componentName + ".top", false) + bottom (rect.getHeight(), componentName + "." + Coordinate::Strings::top) { } RectangleCoordinates::RectangleCoordinates (const String& stringVersion) - : left (true), right (true), top (false), bottom (false) { StringArray tokens; tokens.addTokens (stringVersion, ",", String::empty); @@ -389,22 +469,22 @@ RectangleCoordinates::RectangleCoordinates (const String& stringVersion) bottom = Coordinate (tokens [3], false); } -const Rectangle RectangleCoordinates::resolve (const Coordinate::MarkerResolver& markerResolver) const +const Rectangle RectangleCoordinates::resolve (const Coordinate::NamedCoordinateFinder& nameSource) const { - const int l = roundToInt (left.resolve (markerResolver)); - const int r = roundToInt (right.resolve (markerResolver)); - const int t = roundToInt (top.resolve (markerResolver)); - const int b = roundToInt (bottom.resolve (markerResolver)); + const int l = roundToInt (left.resolve (nameSource)); + const int r = roundToInt (right.resolve (nameSource)); + const int t = roundToInt (top.resolve (nameSource)); + const int b = roundToInt (bottom.resolve (nameSource)); return Rectangle (l, t, r - l, b - t); } -void RectangleCoordinates::moveToAbsolute (const Rectangle& newPos, const Coordinate::MarkerResolver& markerResolver) +void RectangleCoordinates::moveToAbsolute (const Rectangle& newPos, const Coordinate::NamedCoordinateFinder& nameSource) { - left.moveToAbsolute (newPos.getX(), markerResolver); - right.moveToAbsolute (newPos.getRight(), markerResolver); - top.moveToAbsolute (newPos.getY(), markerResolver); - bottom.moveToAbsolute (newPos.getBottom(), markerResolver); + left.moveToAbsolute (newPos.getX(), nameSource); + right.moveToAbsolute (newPos.getRight(), nameSource); + top.moveToAbsolute (newPos.getY(), nameSource); + bottom.moveToAbsolute (newPos.getBottom(), nameSource); } const String RectangleCoordinates::toString() const @@ -413,12 +493,12 @@ const String RectangleCoordinates::toString() const } void RectangleCoordinates::renameAnchorIfUsed (const String& oldName, const String& newName, - const Coordinate::MarkerResolver& markerResolver) + const Coordinate::NamedCoordinateFinder& nameSource) { - left.renameAnchorIfUsed (oldName, newName, markerResolver); - right.renameAnchorIfUsed (oldName, newName, markerResolver); - top.renameAnchorIfUsed (oldName, newName, markerResolver); - bottom.renameAnchorIfUsed (oldName, newName, markerResolver); + left.renameAnchorIfUsed (oldName, newName, nameSource); + right.renameAnchorIfUsed (oldName, newName, nameSource); + top.renameAnchorIfUsed (oldName, newName, nameSource); + bottom.renameAnchorIfUsed (oldName, newName, nameSource); } @@ -455,7 +535,7 @@ void ComponentAutoLayoutManager::setMarker (const String& name, const Coordinate applyLayout(); } -void ComponentAutoLayoutManager::setComponentLayout (Component* comp, const String& name, const RectangleCoordinates& coords) +void ComponentAutoLayoutManager::setComponentBounds (Component* comp, const String& name, const RectangleCoordinates& coords) { jassert (comp != 0); @@ -492,29 +572,26 @@ void ComponentAutoLayoutManager::applyLayout() } } -const Coordinate ComponentAutoLayoutManager::findMarker (const String& name, bool isHorizontal) const +const Coordinate ComponentAutoLayoutManager::findNamedCoordinate (const String& objectName, const String& edge) const { - if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) parent->getWidth(), isHorizontal); - if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) parent->getHeight(), isHorizontal); - - if (name.containsChar ('.')) + if (objectName == Coordinate::Strings::parent) { - const String compName (name.upToFirstOccurrenceOf (".", false, false).trim()); - const String edge (name.fromFirstOccurrenceOf (".", false, false).trim()); + if (edge == Coordinate::Strings::right) return Coordinate ((double) parent->getWidth(), true); + if (edge == Coordinate::Strings::bottom) return Coordinate ((double) parent->getHeight(), false); + } - if (compName.isNotEmpty() && edge.isNotEmpty()) + if (objectName.isNotEmpty() && edge.isNotEmpty()) + { + for (int i = components.size(); --i >= 0;) { - for (int i = components.size(); --i >= 0;) - { - ComponentPosition* c = components.getUnchecked(i); + ComponentPosition* c = components.getUnchecked(i); - if (c->name == compName) - { - if (edge == "left") return c->coords.left; - if (edge == "right") return c->coords.right; - if (edge == "top") return c->coords.top; - if (edge == "bottom") return c->coords.bottom; - } + if (c->name == objectName) + { + if (edge == Coordinate::Strings::left) return c->coords.left; + if (edge == Coordinate::Strings::right) return c->coords.right; + if (edge == Coordinate::Strings::top) return c->coords.top; + if (edge == Coordinate::Strings::bottom) return c->coords.bottom; } } } @@ -523,11 +600,11 @@ const Coordinate ComponentAutoLayoutManager::findMarker (const String& name, boo { MarkerPosition* m = markers.getUnchecked(i); - if (m->markerName == name) + if (m->markerName == objectName) return m->position; } - return Coordinate (isHorizontal); + return Coordinate(); } void ComponentAutoLayoutManager::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) diff --git a/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.h b/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.h index fdf91f18f0..926080097c 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_Coordinate.h @@ -31,15 +31,15 @@ //============================================================================== /** - Holds a co-ordinate along the x or y axis, expressed either as an absolute - position, or relative to other named marker positions. + Describes a coordinate's, either as an absolute position, or relative to + other named positions. */ class Coordinate { public: //============================================================================== /** Creates a zero coordinate. */ - explicit Coordinate (bool isHorizontal); + Coordinate(); /** Recreates a coordinate from its stringified version. */ Coordinate (const String& stringVersion, bool isHorizontal); @@ -47,99 +47,143 @@ public: /** Creates an absolute position from the parent origin. */ Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal); - /** Creates an absolute position relative to a named marker. */ - Coordinate (double absolutePosition, const String& relativeToMarker, bool isHorizontal); + /** Creates an absolute position relative to a named anchor. */ + Coordinate (double absoluteDistanceFromAnchor, const String& anchorPoint); - /** Creates a relative position between two named markers. */ - Coordinate (double relativePosition, const String& marker1, const String& marker2, bool isHorizontal); + /** Creates a relative position between two named points. */ + Coordinate (double relativeProportionBetweenAnchors, const String& anchorPoint1, const String& anchorPoint2); /** Destructor. */ ~Coordinate(); //============================================================================== /** - Provides an interface for looking up the position of a named marker. + Provides an interface for looking up the position of a named anchor. */ - class MarkerResolver + class NamedCoordinateFinder { public: - virtual ~MarkerResolver() {} - virtual const Coordinate findMarker (const String& name, bool isHorizontal) const = 0; + virtual ~NamedCoordinateFinder() {} + virtual const Coordinate findNamedCoordinate (const String& objectName, const String& edge) const = 0; }; + //============================================================================== /** Calculates the absolute position of this co-ordinate. */ - double resolve (const MarkerResolver& markerResolver) const; + double resolve (const NamedCoordinateFinder& nameSource) const; - /** Returns true if this co-ordinate is expressed directly in terms of the specified marker. */ - bool referencesDirectly (const String& markerName) const; - - /** Returns true if this co-ordinate is expressed in terms of the specified marker at any + /** Returns true if this co-ordinate is expressed in terms of the specified coord at any level in its evaluation. */ - bool referencesIndirectly (const String& markerName, const MarkerResolver& markerResolver) const; + bool references (const String& coordName, const NamedCoordinateFinder& nameSource) const; - /** Changes the value of this marker to make it resolve to the specified position. */ - void moveToAbsolute (double newPos, const MarkerResolver& markerResolver); + //============================================================================== + /** Changes the value of this coord to make it resolve to the specified position. */ + void moveToAbsolute (double newPos, const NamedCoordinateFinder& nameSource); - const Coordinate getAnchorPoint1() const; - const Coordinate getAnchorPoint2() const; + /** */ + bool isProportional() const throw() { return anchor2.isNotEmpty(); } - const double getEditableValue() const; - void setEditableValue (const double newValue); + /** */ + void toggleProportionality (const NamedCoordinateFinder& nameSource, bool isHorizontal); - bool isHorizontal() const throw() { return horizontal; } + /** */ + const double getEditableNumber() const; - bool isProportional() const throw() { return isProportion; } - void toggleProportionality (const MarkerResolver& markerResolver); + /** */ + void setEditableNumber (const double newValue); - const String getAnchor1() const { return checkName (anchor1); } - void changeAnchor1 (const String& newMarkerName, const MarkerResolver& markerResolver); + //============================================================================== + /** */ + const String getAnchorName1() const { return anchor1; } - const String getAnchor2() const { return checkName (anchor2); } - void changeAnchor2 (const String& newMarkerName, const MarkerResolver& markerResolver); + /** */ + const String getAnchorName2() const { return anchor2; } + + /** */ + const Coordinate getAnchorCoordinate1() const; + + /** */ + const Coordinate getAnchorCoordinate2() const; + + /** */ + void changeAnchor1 (const String& newAnchor, const NamedCoordinateFinder& nameSource); + + /** */ + void changeAnchor2 (const String& newAnchor, const NamedCoordinateFinder& nameSource); /** Tells the coord that an anchor is changing its name. If the new name is empty, it removes the anchor. */ - void renameAnchorIfUsed (const String& oldName, const String& newName, const MarkerResolver& markerResolver); + void renameAnchorIfUsed (const String& oldName, const String& newName, const NamedCoordinateFinder& nameSource); //============================================================================== /* Position string formats: 123 = absolute pixels from parent origin - marker - marker + 123 - marker - 123 + anchor + anchor + 123 + anchor - 123 50% = percentage between parent origin and parent extent - 50% * marker = percentage between parent origin and marker - 50% * marker1 -> marker2 = percentage between two markers + 50% * anchor = percentage between parent origin and anchor + 50% * anchor1 -> anchor2 = percentage between two named points - standard marker names: + where an anchor name can be: "parent.top", "parent.left", "parent.bottom", "parent.right" - "componentName.top", "componentName.left", "componentName.bottom", "componentName.right" + "objectName.top", "objectName.left", "objectName.bottom", "objectName.right" + "markerName" */ const String toString() const; //============================================================================== - static const char* parentLeftMarkerName; - static const char* parentRightMarkerName; - static const char* parentTopMarkerName; - static const char* parentBottomMarkerName; + struct Strings + { + static const String parent; + static const String left; + static const String right; + static const String top; + static const String bottom; + static const String originX; + static const String originY; + static const String extentX; + static const String extentY; + }; + + //============================================================================== + struct RecursiveCoordinateException : public std::runtime_error + { + RecursiveCoordinateException(); + }; private: //============================================================================== String anchor1, anchor2; double value; - bool isProportion, horizontal; - double resolve (const MarkerResolver& markerResolver, int recursionCounter) const; - double getPosition (const String& name, const MarkerResolver& markerResolver, int recursionCounter) const; - const String checkName (const String& name) const; - const String getOriginMarkerName() const; - const String getExtentMarkerName() const; + double resolve (const NamedCoordinateFinder& nameSource, int recursionCounter) const; + double resolveAnchor (const String& name, const NamedCoordinateFinder& nameSource, int recursionCounter) const; + const String getOriginAnchorName (bool isHorizontal) const throw(); + const String getExtentAnchorName (bool isHorizontal) const throw(); + const Coordinate lookUpName (const String& name, const NamedCoordinateFinder& nameSource) const; static bool isOrigin (const String& name); - static void skipWhitespace (const String& s, int& i); - static const String readMarkerName (const String& s, int& i); - static double readNumber (const String& s, int& i); + static const String getObjectName (const String& fullName); + static const String getEdgeName (const String& fullName); +}; + +//============================================================================== +class CoordinatePair +{ +public: + CoordinatePair(); + CoordinatePair (const Point& absolutePoint); + CoordinatePair (const String& stringVersion); + + const Point resolve (const Coordinate::NamedCoordinateFinder& nameSource) const; + void moveToAbsolute (const Point& newPos, const Coordinate::NamedCoordinateFinder& nameSource); + const String toString() const; + + // Tells the coord that an anchor is changing its name. + void renameAnchorIfUsed (const String& oldName, const String& newName, const Coordinate::NamedCoordinateFinder& nameSource); + + Coordinate x, y; }; //============================================================================== @@ -155,42 +199,58 @@ public: explicit RectangleCoordinates (const String& stringVersion); //============================================================================== - const Rectangle resolve (const Coordinate::MarkerResolver& markerResolver) const; - void moveToAbsolute (const Rectangle& newPos, const Coordinate::MarkerResolver& markerResolver); + const Rectangle resolve (const Coordinate::NamedCoordinateFinder& nameSource) const; + void moveToAbsolute (const Rectangle& newPos, const Coordinate::NamedCoordinateFinder& nameSource); const String toString() const; // Tells the coord that an anchor is changing its name. - void renameAnchorIfUsed (const String& oldName, const String& newName, - const Coordinate::MarkerResolver& markerResolver); + void renameAnchorIfUsed (const String& oldName, const String& newName, const Coordinate::NamedCoordinateFinder& nameSource); Coordinate left, right, top, bottom; }; //============================================================================== +/** +*/ class ComponentAutoLayoutManager : public ComponentListener, - public Coordinate::MarkerResolver, + public Coordinate::NamedCoordinateFinder, public AsyncUpdater { public: //============================================================================== + /** + */ ComponentAutoLayoutManager (Component* parentComponent); + + /** Destructor. */ ~ComponentAutoLayoutManager(); //============================================================================== + /** + */ void setMarker (const String& name, const Coordinate& coord); - void setComponentLayout (Component* comp, const String& name, const RectangleCoordinates& coords); + /** + */ + void setComponentBounds (Component* component, const String& componentName, const RectangleCoordinates& bounds); + /** + */ void applyLayout(); - const Coordinate findMarker (const String& name, bool isHorizontal) const; - + //============================================================================== + /** @internal */ + const Coordinate findNamedCoordinate (const String& objectName, const String& edge) const; + /** @internal */ void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); + /** @internal */ void componentBeingDeleted (Component& component); - + /** @internal */ void handleAsyncUpdate(); + juce_UseDebuggingNewOperator + private: //============================================================================== struct ComponentPosition @@ -213,6 +273,10 @@ private: Component* parent; OwnedArray components; OwnedArray markers; + + ComponentAutoLayoutManager (const ComponentAutoLayoutManager&); + ComponentAutoLayoutManager& operator= (const ComponentAutoLayoutManager&); }; + #endif // __JUCER_COORDINATE_H_EF56ACFA__ diff --git a/extras/Jucer (experimental)/Source/utility/jucer_CoordinatePropertyComponent.h b/extras/Jucer (experimental)/Source/utility/jucer_CoordinatePropertyComponent.h index dda70f90b6..bd64193589 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_CoordinatePropertyComponent.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_CoordinatePropertyComponent.h @@ -34,9 +34,9 @@ class CoordinatePropertyComponent : public PropertyComponent, { public: //============================================================================== - CoordinatePropertyComponent (Coordinate::MarkerResolver& resolver_, const String& name, + CoordinatePropertyComponent (Coordinate::NamedCoordinateFinder& nameSource_, const String& name, const Value& coordValue_, bool isHorizontal_) - : PropertyComponent (name, 40), resolver (resolver_), + : PropertyComponent (name, 40), nameSource (nameSource_), coordValue (coordValue_), textValue (Value (new CoordEditableValueSource (coordValue_, isHorizontal_))), isHorizontal (isHorizontal_) @@ -98,26 +98,26 @@ public: if (button == proportionButton) { - coord.toggleProportionality (resolver); + coord.toggleProportionality (nameSource, isHorizontal); coordValue = coord.toString(); } else if (button == anchorButton1) { - const String marker (pickMarker (anchorButton1, coord.getAnchor1(), true)); + const String marker (pickMarker (anchorButton1, coord.getAnchorName1(), true)); if (marker.isNotEmpty()) { - coord.changeAnchor1 (marker, resolver); + coord.changeAnchor1 (marker, nameSource); coordValue = coord.toString(); } } else if (button == anchorButton2) { - const String marker (pickMarker (anchorButton2, coord.getAnchor2(), false)); + const String marker (pickMarker (anchorButton2, coord.getAnchorName2(), false)); if (marker.isNotEmpty()) { - coord.changeAnchor2 (marker, resolver); + coord.changeAnchor2 (marker, nameSource); coordValue = coord.toString(); } } @@ -127,10 +127,10 @@ public: { Coordinate coord (getCoordinate()); - anchorButton1->setButtonText (coord.getAnchor1()); + anchorButton1->setButtonText (coord.getAnchorName1()); anchorButton2->setVisible (coord.isProportional()); - anchorButton2->setButtonText (coord.getAnchor2()); + anchorButton2->setButtonText (coord.getAnchorName2()); resized(); } @@ -142,7 +142,7 @@ public: virtual const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) = 0; protected: - Coordinate::MarkerResolver& resolver; + Coordinate::NamedCoordinateFinder& nameSource; Value coordValue, textValue; Label* label; TextButton* proportionButton; @@ -168,15 +168,15 @@ protected: Coordinate coord (sourceValue.toString(), isHorizontal); if (coord.isProportional()) - return String (coord.getEditableValue()) + "%"; + return String (coord.getEditableNumber()) + "%"; - return coord.getEditableValue(); + return coord.getEditableNumber(); } void setValue (const var& newValue) { Coordinate coord (sourceValue.toString(), isHorizontal); - coord.setEditableValue ((double) newValue); + coord.setEditableNumber ((double) newValue); const String newVal (coord.toString()); if (sourceValue != newVal) diff --git a/extras/Jucer (experimental)/Source/utility/jucer_MarkerListBase.h b/extras/Jucer (experimental)/Source/utility/jucer_MarkerListBase.h index a6ce2d737d..46922259e7 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_MarkerListBase.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_MarkerListBase.h @@ -30,7 +30,7 @@ //============================================================================== -class MarkerListBase : public Coordinate::MarkerResolver +class MarkerListBase : public Coordinate::NamedCoordinateFinder { public: MarkerListBase (const ValueTree& group_, bool isX_) : group (group_), isX (isX_) {} @@ -133,10 +133,10 @@ public: { public: //============================================================================== - PositionPropertyComponent (MarkerResolver& resolver_, MarkerListBase& markerList_, + PositionPropertyComponent (NamedCoordinateFinder& nameSource_, MarkerListBase& markerList_, const String& name, const ValueTree& markerState_, const Value& coordValue_) - : CoordinatePropertyComponent (resolver_, name, coordValue_, markerList_.isHorizontal()), + : CoordinatePropertyComponent (nameSource_, name, coordValue_, markerList_.isHorizontal()), markerList (markerList_), markerState (markerState_) { diff --git a/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.h b/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.h index 85879d6400..b7a368af56 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_MiscUtilities.h @@ -80,3 +80,59 @@ private: Colour colour; GlyphArrangement glyphs; }; + +//============================================================================== +class JucerToolbarButton : public ToolbarItemComponent +{ +public: + //============================================================================== + JucerToolbarButton (int itemId_, const String& labelText) + : ToolbarItemComponent (itemId_, labelText, true) + { + setClickingTogglesState (false); + } + + ~JucerToolbarButton() + { + } + + //============================================================================== + bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize, int& minSize, int& maxSize) + { + preferredSize = minSize = maxSize = 50; + return true; + } + + void paintButton (Graphics& g, bool over, bool down) + { + Path p; + p.addRoundedRectangle (1.5f, 2.5f, getWidth() - 3.0f, getHeight() - 5.0f, 3.0f); + + if (getToggleState()) + { + g.setColour (Colours::grey.withAlpha (0.5f)); + g.fillPath (p); + } + + g.setColour (Colours::darkgrey.withAlpha (0.3f)); + g.strokePath (p, PathStrokeType (1.0f)); + + g.setFont (11.0f); + g.setColour (Colours::black.withAlpha ((over || down) ? 1.0f : 0.7f)); + g.drawFittedText (getButtonText(), 2, 2, getWidth() - 4, getHeight() - 4, Justification::centred, 2); + } + + void paintButtonArea (Graphics& g, int width, int height, bool isMouseOver, bool isMouseDown) + { + } + + void contentAreaChanged (const Rectangle& newBounds) + { + } + + juce_UseDebuggingNewOperator + +private: + JucerToolbarButton (const JucerToolbarButton&); + JucerToolbarButton& operator= (const JucerToolbarButton&); +}; diff --git a/extras/juce demo/Source/demos/RenderingTestComponent.cpp b/extras/juce demo/Source/demos/RenderingTestComponent.cpp index 8a2b629ef3..0b6b7492f7 100644 --- a/extras/juce demo/Source/demos/RenderingTestComponent.cpp +++ b/extras/juce demo/Source/demos/RenderingTestComponent.cpp @@ -296,7 +296,7 @@ private: void drawSVG (Graphics& g) { - if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 3000) + if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 2000) { lastSVGLoadTime = Time::getCurrentTime(); createSVGDrawable(); @@ -344,22 +344,23 @@ private: if (svgFileStream != 0) { - Drawable* loadedSVG = Drawable::createFromImageDataStream (*svgFileStream); + svgDrawable = dynamic_cast (Drawable::createFromImageDataStream (*svgFileStream)); delete svgFileStream; - if (loadedSVG != 0) + if (svgDrawable != 0) { // to make our icon the right size, we'll put it inside a DrawableComposite, and apply // a transform to get it to the size we want. - Rectangle bounds = loadedSVG->getBounds(); - const float scaleFactor = 300.0f / jmax (bounds.getWidth(), bounds.getHeight()); + Rectangle bounds = svgDrawable->getBounds(); + const float scaleFactor = 200.0f / jmax (bounds.getWidth(), bounds.getHeight()); - svgDrawable = new DrawableComposite(); - svgDrawable->insertDrawable (loadedSVG, - AffineTransform::translation (-bounds.getCentreX(), - -bounds.getCentreY()) - .scaled (scaleFactor, scaleFactor)); + Point topLeft (-bounds.getCentreX() * scaleFactor, + -bounds.getCentreY() * scaleFactor); + + svgDrawable->setTransform (topLeft, + topLeft + Point (scaleFactor, 0), + topLeft + Point (0, scaleFactor)); } } } diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 9de63538bd..a340af96c7 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -16490,9 +16490,9 @@ bool ValueTree::hasType (const Identifier& typeName) const return object != 0 && object->type == typeName; } -const String ValueTree::getType() const +const Identifier ValueTree::getType() const { - return object != 0 ? object->type.toString() : String::empty; + return object != 0 ? object->type : Identifier(); } ValueTree ValueTree::getParent() const @@ -16726,7 +16726,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) void ValueTree::writeToStream (OutputStream& output) { - output.writeString (getType()); + output.writeString (getType().toString()); const int numProps = getNumProperties(); output.writeCompressedInt (numProps); @@ -21160,7 +21160,7 @@ public: { bufferList.calloc (256, 1); -#ifdef WIN32 +#if JUCE_WINDOWS if (InitializeQTML (0) != noErr) return; #endif @@ -41278,14 +41278,16 @@ bool Component::isFocusContainer() const throw() return flags.isFocusContainerFlag; } +static const Identifier juce_explicitFocusOrderId ("_jexfo"); + int Component::getExplicitFocusOrder() const { - return properties ["_jexfo"]; + return properties [juce_explicitFocusOrderId]; } void Component::setExplicitFocusOrder (const int newFocusOrderIndex) { - properties.set ("_jexfo", newFocusOrderIndex); + properties.set (juce_explicitFocusOrderId, newFocusOrderIndex); } KeyboardFocusTraverser* Component::createFocusTraverser() @@ -47985,11 +47987,7 @@ Image* ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) { const Point pos (rowComp->relativePositionToOtherComponent (this, Point())); const Rectangle rowRect (pos.getX(), pos.getY(), rowComp->getWidth(), rowComp->getHeight()); - - if (imageArea.isEmpty()) - imageArea = rowRect; - else - imageArea = imageArea.getUnion (rowRect); + imageArea = imageArea.getUnion (rowRect); } } @@ -62995,6 +62993,8 @@ TabBarButton* TabbedComponent::createTabButton (const String& tabName, const int return new TabBarButton (tabName, tabs, tabIndex); } +const Identifier TabbedComponent::deleteComponentId ("deleteByTabComp_"); + void TabbedComponent::clearTabs() { if (panelComponent != 0) @@ -63013,7 +63013,7 @@ void TabbedComponent::clearTabs() // be careful not to delete these components until they've been removed from the tab component jassert (c == 0 || c->isValidComponent()); - if (c != 0 && (bool) c->getProperties() ["deleteByTabComp_"]) + if (c != 0 && (bool) c->getProperties() [deleteComponentId]) delete c; } @@ -63029,7 +63029,7 @@ void TabbedComponent::addTab (const String& tabName, contentComponents.insert (insertIndex, contentComponent); if (contentComponent != 0) - contentComponent->getProperties().set ("deleteByTabComp_", deleteComponentWhenNotNeeded); + contentComponent->getProperties().set (deleteComponentId, deleteComponentWhenNotNeeded); tabs->addTab (tabName, tabBackgroundColour, insertIndex); } @@ -63043,7 +63043,7 @@ void TabbedComponent::removeTab (const int tabIndex) { Component* const c = contentComponents [tabIndex]; - if (c != 0 && (bool) c->getProperties() ["deleteByTabComp_"]) + if (c != 0 && (bool) c->getProperties() [deleteComponentId]) { if (c == panelComponent) panelComponent = 0; @@ -70383,8 +70383,7 @@ public: { if (mouseDowns[0].time - mouseDowns[i].time < (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))) && abs (mouseDowns[0].position.getX() - mouseDowns[i].position.getX()) < 8 - && abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8 - && mouseDowns[0].component == mouseDowns[i].component) + && abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8) { ++numClicks; } @@ -78849,6 +78848,18 @@ ColourGradient::~ColourGradient() { } +bool ColourGradient::operator== (const ColourGradient& other) const throw() +{ + return point1 == other.point1 && point2 == other.point2 + && isRadial == other.isRadial + && colours == other.colours; +} + +bool ColourGradient::operator!= (const ColourGradient& other) const throw() +{ + return ! operator== (other); +} + void ColourGradient::clearColours() { colours.clear(); @@ -83678,6 +83689,8 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_Drawable.cpp ***/ BEGIN_JUCE_NAMESPACE +const Identifier Drawable::idProperty ("id"); + Drawable::RenderingContext::RenderingContext (Graphics& g_, const AffineTransform& transform_, const float opacity_) throw() @@ -83771,22 +83784,22 @@ Drawable* Drawable::createFromImageFile (const File& file) return fin != 0 ? createFromImageDataStream (*fin) : 0; } -Drawable* Drawable::createFromValueTree (const ValueTree& tree) +Drawable* Drawable::createFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - Drawable* d = DrawablePath::createFromValueTree (tree); + const Identifier type (tree.getType()); - if (d == 0) - { - d = DrawableComposite::createFromValueTree (tree); + Drawable* d = 0; + if (type == DrawablePath::valueTreeType) + d = new DrawablePath(); + else if (type == DrawableComposite::valueTreeType) + d = new DrawableComposite(); + else if (type == DrawableImage::valueTreeType) + d = new DrawableImage(); + else if (type == DrawableText::valueTreeType) + d = new DrawableText(); - if (d == 0) - { - d = DrawableImage::createFromValueTree (tree); - - if (d == 0) - d = DrawableText::createFromValueTree (tree); - } - } + if (d != 0) + d->refreshFromValueTree (tree, imageProvider); return d; } @@ -83800,54 +83813,53 @@ BEGIN_JUCE_NAMESPACE DrawableComposite::DrawableComposite() { + controlPoints[1].setXY (1.0f, 0.0f); + controlPoints[2].setXY (0.0f, 1.0f); } DrawableComposite::~DrawableComposite() { } -void DrawableComposite::insertDrawable (Drawable* drawable, - const AffineTransform& transform, - const int index) +void DrawableComposite::insertDrawable (Drawable* drawable, const int index) { if (drawable != 0) { - if (! drawables.contains (drawable)) - { - drawables.insert (index, drawable); - - if (transform.isIdentity()) - transforms.insert (index, 0); - else - transforms.insert (index, new AffineTransform (transform)); - } - else - { - jassertfalse; // trying to add a drawable that's already in here! - } + jassert (! drawables.contains (drawable)); // trying to add a drawable that's already in here! + drawables.insert (index, drawable); } } -void DrawableComposite::insertDrawable (const Drawable& drawable, - const AffineTransform& transform, - const int index) +void DrawableComposite::insertDrawable (const Drawable& drawable, const int index) { - insertDrawable (drawable.createCopy(), transform, index); + insertDrawable (drawable.createCopy(), index); } void DrawableComposite::removeDrawable (const int index, const bool deleteDrawable) { drawables.remove (index, deleteDrawable); - transforms.remove (index); } void DrawableComposite::bringToFront (const int index) { if (index >= 0 && index < drawables.size() - 1) - { drawables.move (index, -1); - transforms.move (index, -1); - } +} + +void DrawableComposite::setTransform (const Point& targetPositionForOrigin, + const Point& targetPositionForX1Y0, + const Point& targetPositionForX0Y1) +{ + controlPoints[0] = targetPositionForOrigin; + controlPoints[1] = targetPositionForX1Y0; + controlPoints[2] = targetPositionForX0Y1; +} + +const AffineTransform DrawableComposite::getTransform() const +{ + return AffineTransform::fromTargetPoints (controlPoints[0].getX(), controlPoints[0].getY(), + controlPoints[1].getX(), controlPoints[1].getY(), + controlPoints[2].getX(), controlPoints[2].getY()); } void DrawableComposite::render (const Drawable::RenderingContext& context) const @@ -83857,15 +83869,10 @@ void DrawableComposite::render (const Drawable::RenderingContext& context) const if (context.opacity >= 1.0f || drawables.size() == 1) { Drawable::RenderingContext contextCopy (context); + contextCopy.transform = getTransform().followedBy (context.transform); for (int i = 0; i < drawables.size(); ++i) - { - const AffineTransform* const t = transforms.getUnchecked(i); - contextCopy.transform = (t == 0) ? context.transform - : t->followedBy (context.transform); - drawables.getUnchecked(i)->render (contextCopy); - } } else { @@ -83888,42 +83895,28 @@ void DrawableComposite::render (const Drawable::RenderingContext& context) const } } -const Rectangle DrawableComposite::getBounds() const +const Rectangle DrawableComposite::getUntransformedBounds() const { Rectangle bounds; for (int i = 0; i < drawables.size(); ++i) - { - const Drawable* const d = drawables.getUnchecked(i); - const AffineTransform* const t = transforms.getUnchecked(i); - - const Rectangle childBounds (t == 0 ? d->getBounds() - : d->getBounds().transformed (*t)); - - if (bounds.isEmpty()) - bounds = childBounds; - else if (! childBounds.isEmpty()) - bounds = bounds.getUnion (childBounds); - } + bounds = bounds.getUnion (drawables.getUnchecked(i)->getBounds()); return bounds; } +const Rectangle DrawableComposite::getBounds() const +{ + return getUntransformedBounds().transformed (getTransform()); +} + bool DrawableComposite::hitTest (float x, float y) const { + getTransform().inverted().transformPoint (x, y); + for (int i = 0; i < drawables.size(); ++i) - { - float tx = x; - float ty = y; - - const AffineTransform* const t = transforms.getUnchecked(i); - - if (t != 0) - t->inverted().transformPoint (tx, ty); - - if (drawables.getUnchecked(i)->hitTest (tx, ty)) + if (drawables.getUnchecked(i)->hitTest (x, y)) return true; - } return false; } @@ -83932,86 +83925,122 @@ Drawable* DrawableComposite::createCopy() const { DrawableComposite* const dc = new DrawableComposite(); - for (int i = 0; i < drawables.size(); ++i) - { - dc->drawables.add (drawables.getUnchecked(i)->createCopy()); + for (int i = 0; i < 3; ++i) + dc->controlPoints[i] = controlPoints[i]; - const AffineTransform* const t = transforms.getUnchecked(i); - dc->transforms.add (t != 0 ? new AffineTransform (*t) : 0); - } + for (int i = 0; i < drawables.size(); ++i) + dc->drawables.add (drawables.getUnchecked(i)->createCopy()); return dc; } -ValueTree DrawableComposite::createValueTree() const +const Identifier DrawableComposite::valueTreeType ("Group"); + +namespace DrawableCompositeHelpers { - ValueTree v ("Group"); + static const Identifier topLeft ("topLeft"); + static const Identifier topRight ("topRight"); + static const Identifier bottomLeft ("bottomLeft"); - if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); - - for (int i = 0; i < drawables.size(); ++i) + static void stringToPoint (const String& coords, Point& point) { - Drawable* const d = drawables.getUnchecked(i); - - ValueTree child (d->createValueTree()); - - AffineTransform* transform = transforms.getUnchecked(i); - - if (transform != 0) + if (coords.isNotEmpty()) { - String t; - t << transform->mat00 << " " << transform->mat01 << " " << transform->mat02 << " " - << transform->mat10 << " " << transform->mat11 << " " << transform->mat12; - - child.setProperty ("transform", t, 0); + const int comma = coords.indexOfChar (','); + point.setXY (coords.substring (0, comma).getFloatValue(), + coords.substring (comma).getFloatValue()); } - - v.addChild (child, -1, 0); } - return v; + static const var pointToString (const Point& point) + { + return String (point.getX()) + ", " + String (point.getY()); + } } -DrawableComposite* DrawableComposite::createFromValueTree (const ValueTree& tree) +const Rectangle DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - if (! tree.hasType ("Group")) - return 0; + jassert (tree.hasType (valueTreeType)); - DrawableComposite* dc = new DrawableComposite(); - dc->setName (tree ["id"]); + Rectangle damageRect; + + setName (tree [idProperty]); + + Point newControlPoint[3]; + DrawableCompositeHelpers::stringToPoint (tree [DrawableCompositeHelpers::topLeft].toString(), newControlPoint[0]); + DrawableCompositeHelpers::stringToPoint (tree [DrawableCompositeHelpers::topRight].toString(), newControlPoint[1]); + DrawableCompositeHelpers::stringToPoint (tree [DrawableCompositeHelpers::bottomLeft].toString(), newControlPoint[2]); + bool controlPointsChanged = false; + + if (controlPoints[0] != newControlPoint[0] + || controlPoints[1] != newControlPoint[1] + || controlPoints[2] != newControlPoint[2]) + { + controlPointsChanged = true; + damageRect = getUntransformedBounds(); + controlPoints[0] = newControlPoint[0]; + controlPoints[1] = newControlPoint[1]; + controlPoints[2] = newControlPoint[2]; + } + + int i; + for (i = drawables.size(); --i >= tree.getNumChildren();) + { + damageRect = damageRect.getUnion (drawables.getUnchecked(i)->getBounds()); + drawables.remove (i); + } for (int i = 0; i < tree.getNumChildren(); ++i) { - ValueTree childTree (tree.getChild (i)); - Drawable* d = Drawable::createFromValueTree (childTree); + const ValueTree childTree (tree.getChild (i)); + Drawable* d = drawables[i]; if (d != 0) { - AffineTransform transform; - const String transformAtt (childTree ["transform"].toString()); - - if (transformAtt.isNotEmpty()) + if (childTree.hasType (d->getValueTreeType())) { - StringArray tokens; - tokens.addTokens (transformAtt.trim(), false); - tokens.removeEmptyStrings (true); - - if (tokens.size() == 6) - { - float f[6]; - for (int j = 0; j < 6; ++j) - f[j] = (float) tokens[j].getDoubleValue(); - - transform = AffineTransform (f[0], f[1], f[2], f[3], f[4], f[5]); - } + damageRect = damageRect.getUnion (d->refreshFromValueTree (childTree, imageProvider)); } - - dc->insertDrawable (d, transform); + else + { + damageRect = damageRect.getUnion (d->getBounds()); + d = createFromValueTree (childTree, imageProvider); + drawables.set (i, d); + damageRect = damageRect.getUnion (d->getBounds()); + } + } + else + { + d = createFromValueTree (childTree, imageProvider); + drawables.set (i, d); + damageRect = damageRect.getUnion (d->getBounds()); } } - return dc; + if (controlPointsChanged) + damageRect = damageRect.getUnion (getUntransformedBounds()); + + return damageRect.transformed (getTransform()); +} + +const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider) const +{ + ValueTree v (valueTreeType); + + if (getName().isNotEmpty()) + v.setProperty (idProperty, getName(), 0); + + if (! getTransform().isIdentity()) + { + v.setProperty (DrawableCompositeHelpers::topLeft, DrawableCompositeHelpers::pointToString (controlPoints[0]), 0); + v.setProperty (DrawableCompositeHelpers::topRight, DrawableCompositeHelpers::pointToString (controlPoints[1]), 0); + v.setProperty (DrawableCompositeHelpers::bottomLeft, DrawableCompositeHelpers::pointToString (controlPoints[2]), 0); + } + + for (int i = 0; i < drawables.size(); ++i) + v.addChild (drawables.getUnchecked(i)->createValueTree (imageProvider), -1, 0); + + return v; } END_JUCE_NAMESPACE @@ -84027,6 +84056,8 @@ DrawableImage::DrawableImage() opacity (1.0f), overlayColour (0x00000000) { + controlPoints[1].setXY (1.0f, 0.0f); + controlPoints[2].setXY (0.0f, 1.0f); } DrawableImage::~DrawableImage() @@ -84047,6 +84078,10 @@ void DrawableImage::setImage (const Image& imageToCopy) clearImage(); image = new Image (imageToCopy); canDeleteImage = true; + + controlPoints[0].setXY (0.0f, 0.0f); + controlPoints[1].setXY ((float) image->getWidth(), 0.0f); + controlPoints[2].setXY (0.0f, (float) image->getHeight()); } void DrawableImage::setImage (Image* imageToUse, @@ -84055,6 +84090,13 @@ void DrawableImage::setImage (Image* imageToUse, clearImage(); image = imageToUse; canDeleteImage = releaseWhenNotNeeded; + + if (image != 0) + { + controlPoints[0].setXY (0.0f, 0.0f); + controlPoints[1].setXY ((float) image->getWidth(), 0.0f); + controlPoints[2].setXY (0.0f, (float) image->getHeight()); + } } void DrawableImage::setOpacity (const float newOpacity) @@ -84067,22 +84109,44 @@ void DrawableImage::setOverlayColour (const Colour& newOverlayColour) overlayColour = newOverlayColour; } +void DrawableImage::setTransform (const Point& imageTopLeftPosition, + const Point& imageTopRightPosition, + const Point& imageBottomLeftPosition) +{ + controlPoints[0] = imageTopLeftPosition; + controlPoints[1] = imageTopRightPosition; + controlPoints[2] = imageBottomLeftPosition; +} + +const AffineTransform DrawableImage::getTransform() const +{ + if (image == 0) + return AffineTransform::identity; + + const Point tr (controlPoints[0] + (controlPoints[1] - controlPoints[0]) / image->getWidth()); + const Point bl (controlPoints[0] + (controlPoints[2] - controlPoints[0]) / image->getHeight()); + + return AffineTransform::fromTargetPoints (controlPoints[0].getX(), controlPoints[0].getY(), + tr.getX(), tr.getY(), + bl.getX(), bl.getY()); +} + void DrawableImage::render (const Drawable::RenderingContext& context) const { if (image != 0) { + const AffineTransform t (getTransform().followedBy (context.transform)); + if (opacity > 0.0f && ! overlayColour.isOpaque()) { context.g.setOpacity (context.opacity * opacity); - context.g.drawImageTransformed (image, image->getBounds(), - context.transform, false); + context.g.drawImageTransformed (image, image->getBounds(), t, false); } if (! overlayColour.isTransparent()) { context.g.setColour (overlayColour.withMultipliedAlpha (context.opacity)); - context.g.drawImageTransformed (image, image->getBounds(), - context.transform, true); + context.g.drawImageTransformed (image, image->getBounds(), t, true); } } } @@ -84092,17 +84156,38 @@ const Rectangle DrawableImage::getBounds() const if (image == 0) return Rectangle(); - return Rectangle (0, 0, (float) image->getWidth(), (float) image->getHeight()); + const Point bottomRight (controlPoints[1] + (controlPoints[2] - controlPoints[0])); + float minX = bottomRight.getX(); + float maxX = minX; + float minY = bottomRight.getY(); + float maxY = minY; + + for (int i = 0; i < 3; ++i) + { + minX = jmin (minX, controlPoints[i].getX()); + maxX = jmax (maxX, controlPoints[i].getX()); + minY = jmin (minY, controlPoints[i].getY()); + maxY = jmax (maxY, controlPoints[i].getY()); + } + + return Rectangle (minX, minY, maxX - minX, maxY - minY); } bool DrawableImage::hitTest (float x, float y) const { - return image != 0 - && x >= 0.0f - && y >= 0.0f - && x < image->getWidth() - && y < image->getHeight() - && image->getPixelAt (roundToInt (x), roundToInt (y)).getAlpha() >= 127; + if (image == 0) + return false; + + getTransform().inverted().transformPoint (x, y); + + const int ix = roundToInt (x); + const int iy = roundToInt (y); + + return ix >= 0 + && iy >= 0 + && ix < image->getWidth() + && iy < image->getHeight() + && image->getPixelAt (ix, iy).getAlpha() >= 127; } Drawable* DrawableImage::createCopy() const @@ -84112,6 +84197,9 @@ Drawable* DrawableImage::createCopy() const di->opacity = opacity; di->overlayColour = overlayColour; + for (int i = 0; i < 4; ++i) + di->controlPoints[i] = controlPoints[i]; + if (image != 0) { if ((! canDeleteImage) || ! ImageCache::isImageInCache (image)) @@ -84128,59 +84216,110 @@ Drawable* DrawableImage::createCopy() const return di; } -ValueTree DrawableImage::createValueTree() const +const Identifier DrawableImage::valueTreeType ("Image"); + +namespace DrawableImageHelpers { - ValueTree v ("Image"); + static const Identifier opacity ("opacity"); + static const Identifier overlay ("overlay"); + static const Identifier image ("image"); + static const Identifier topLeft ("topLeft"); + static const Identifier topRight ("topRight"); + static const Identifier bottomLeft ("bottomLeft"); - if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); - - if (opacity < 1.0f) - v.setProperty ("opacity", (double) opacity, 0); - - if (! overlayColour.isTransparent()) - v.setProperty ("overlay", String::toHexString ((int) overlayColour.getARGB()), 0); - - if (image != 0) + static void stringToPoint (const String& coords, Point& point) { - MemoryOutputStream imageData; - PNGImageFormat pngFormat; - if (pngFormat.writeImageToStream (*image, imageData)) + if (coords.isNotEmpty()) { - String base64 (MemoryBlock (imageData.getData(), imageData.getDataSize()).toBase64Encoding()); - - for (int i = (base64.length() & ~127); i >= 0; i -= 128) - base64 = base64.substring (0, i) + "\n" + base64.substring (i); - - v.setProperty ("data", base64, 0); + const int comma = coords.indexOfChar (','); + point.setXY (coords.substring (0, comma).getFloatValue(), + coords.substring (comma).getFloatValue()); } } - return v; + static const var pointToString (const Point& point) + { + return String (point.getX()) + ", " + String (point.getY()); + } } -DrawableImage* DrawableImage::createFromValueTree (const ValueTree& tree) +const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - if (! tree.hasType ("Image")) - return 0; + jassert (tree.hasType (valueTreeType)); - DrawableImage* di = new DrawableImage(); + setName (tree [idProperty]); - di->setName (tree ["id"]); - di->opacity = tree.hasProperty ("opacity") ? (float) tree ["opacity"] : 1.0f; - di->overlayColour = Colour (tree ["overlay"].toString().getHexValue32()); + const float newOpacity = tree.getProperty (DrawableImageHelpers::opacity, 1.0); + const Colour newOverlayColour (tree [DrawableImageHelpers::overlay].toString().getHexValue32()); - MemoryBlock imageData; - if (imageData.fromBase64Encoding (tree ["data"])) + Image* newImage = 0; + const String imageIdentifier (tree [DrawableImageHelpers::image].toString()); + if (imageIdentifier.isNotEmpty()) { - Image* const im = ImageFileFormat::loadFrom (imageData.getData(), (int) imageData.getSize()); - if (im == 0) - return false; + jassert (imageProvider != 0); // if you're using images, you need to provide something that can load and save them! - di->setImage (im, true); + if (imageProvider != 0) + newImage = imageProvider->getImageForIdentifier (imageIdentifier); } - return di; + Point newControlPoint[3]; + DrawableImageHelpers::stringToPoint (tree [DrawableImageHelpers::topLeft].toString(), newControlPoint[0]); + DrawableImageHelpers::stringToPoint (tree [DrawableImageHelpers::topRight].toString(), newControlPoint[1]); + DrawableImageHelpers::stringToPoint (tree [DrawableImageHelpers::bottomLeft].toString(), newControlPoint[2]); + + if (newOpacity != opacity || overlayColour != newOverlayColour || image != newImage + || controlPoints[0] != newControlPoint[0] + || controlPoints[1] != newControlPoint[1] + || controlPoints[2] != newControlPoint[2]) + { + opacity = newOpacity; + overlayColour = newOverlayColour; + controlPoints[0] = newControlPoint[0]; + controlPoints[1] = newControlPoint[1]; + controlPoints[2] = newControlPoint[2]; + + if (image != newImage) + { + ImageCache::release (image); + image = newImage; + } + + return getBounds(); + } + + ImageCache::release (newImage); + return Rectangle(); +} + +const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const +{ + ValueTree v (valueTreeType); + + if (getName().isNotEmpty()) + v.setProperty (idProperty, getName(), 0); + + if (opacity < 1.0f) + v.setProperty (DrawableImageHelpers::opacity, (double) opacity, 0); + + if (! overlayColour.isTransparent()) + v.setProperty (DrawableImageHelpers::overlay, String::toHexString ((int) overlayColour.getARGB()), 0); + + if (! getTransform().isIdentity()) + { + v.setProperty (DrawableImageHelpers::topLeft, DrawableImageHelpers::pointToString (controlPoints[0]), 0); + v.setProperty (DrawableImageHelpers::topRight, DrawableImageHelpers::pointToString (controlPoints[1]), 0); + v.setProperty (DrawableImageHelpers::bottomLeft, DrawableImageHelpers::pointToString (controlPoints[2]), 0); + } + + if (image != 0) + { + jassert (imageProvider != 0); // if you're using images, you need to provide something that can load and save them! + + if (imageProvider != 0) + v.setProperty (DrawableImageHelpers::image, imageProvider->getIdentifierForImage (image), 0); + } + + return v; } END_JUCE_NAMESPACE @@ -84284,120 +84423,160 @@ Drawable* DrawablePath::createCopy() const return dp; } -static const FillType readFillTypeFromTree (const ValueTree& v) +const Identifier DrawablePath::valueTreeType ("Path"); + +namespace DrawablePathHelpers { - const String type (v["type"].toString()); + static const Identifier type ("type"); + static const Identifier solid ("solid"); + static const Identifier colour ("colour"); + static const Identifier gradient ("gradient"); + static const Identifier x1 ("x1"); + static const Identifier x2 ("x2"); + static const Identifier y1 ("y1"); + static const Identifier y2 ("y2"); + static const Identifier radial ("radial"); + static const Identifier colours ("colours"); + static const Identifier fill ("fill"); + static const Identifier stroke ("stroke"); + static const Identifier jointStyle ("jointStyle"); + static const Identifier capStyle ("capStyle"); + static const Identifier strokeWidth ("strokeWidth"); + static const Identifier path ("path"); - if (type.equalsIgnoreCase ("solid")) + static bool updateFillType (const ValueTree& v, FillType& fillType) { - const String colour (v ["colour"].toString()); - return Colour (colour.isEmpty() ? (uint32) 0xff000000 - : (uint32) colour.getHexValue32()); - } - else if (type.equalsIgnoreCase ("gradient")) - { - ColourGradient g; - g.point1.setXY (v["x1"], v["y1"]); - g.point2.setXY (v["x2"], v["y2"]); - g.isRadial = v["radial"]; + const String type (v[type].toString()); - StringArray colours; - colours.addTokens (v["colours"].toString(), false); + if (type.equalsIgnoreCase (solid)) + { + const String colourString (v [colour].toString()); + const Colour newColour (colourString.isEmpty() ? (uint32) 0xff000000 + : (uint32) colourString.getHexValue32()); - for (int i = 0; i < colours.size() / 2; ++i) - g.addColour (colours[i * 2].getDoubleValue(), - Colour ((uint32) colours[i * 2 + 1].getHexValue32())); + if (fillType.isColour() && fillType.colour == newColour) + return false; - return g; + fillType.setColour (newColour); + return true; + } + else if (type.equalsIgnoreCase (gradient)) + { + ColourGradient g; + g.point1.setXY (v[x1], v[y1]); + g.point2.setXY (v[x2], v[y2]); + g.isRadial = v[radial]; + + StringArray colourSteps; + colourSteps.addTokens (v[colours].toString(), false); + + for (int i = 0; i < colourSteps.size() / 2; ++i) + g.addColour (colourSteps[i * 2].getDoubleValue(), + Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32())); + + if (fillType.isGradient() && *fillType.gradient == g) + return false; + + fillType.setGradient (g); + return true; + } + + jassertfalse; + return false; } - jassertfalse; - return FillType(); + static ValueTree createFillType (const Identifier& tagName, const FillType& fillType) + { + ValueTree v (tagName); + + if (fillType.isColour()) + { + v.setProperty (type, "solid", 0); + v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), 0); + } + else if (fillType.isGradient()) + { + v.setProperty (type, "gradient", 0); + v.setProperty (x1, fillType.gradient->point1.getX(), 0); + v.setProperty (y1, fillType.gradient->point1.getY(), 0); + v.setProperty (x2, fillType.gradient->point2.getX(), 0); + v.setProperty (y2, fillType.gradient->point2.getY(), 0); + v.setProperty (radial, fillType.gradient->isRadial, 0); + + String s; + for (int i = 0; i < fillType.gradient->getNumColours(); ++i) + s << " " << fillType.gradient->getColourPosition (i) + << " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); + + v.setProperty (colours, s.trimStart(), 0); + } + else + { + jassertfalse; //xxx + } + + return v; + } } -static ValueTree createTreeForFillType (const String& tagName, const FillType& fillType) +const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - ValueTree v (tagName); + jassert (tree.hasType (valueTreeType)); - if (fillType.isColour()) - { - v.setProperty ("type", "solid", 0); - v.setProperty ("colour", String::toHexString ((int) fillType.colour.getARGB()), 0); - } - else if (fillType.isGradient()) - { - v.setProperty ("type", "gradient", 0); - v.setProperty ("x1", fillType.gradient->point1.getX(), 0); - v.setProperty ("y1", fillType.gradient->point1.getY(), 0); - v.setProperty ("x2", fillType.gradient->point2.getX(), 0); - v.setProperty ("y2", fillType.gradient->point2.getY(), 0); - v.setProperty ("radial", fillType.gradient->isRadial, 0); + Rectangle damageRect; + setName (tree [idProperty]); - String s; - for (int i = 0; i < fillType.gradient->getNumColours(); ++i) - s << " " << fillType.gradient->getColourPosition (i) - << " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); + bool needsRedraw = DrawablePathHelpers::updateFillType (tree.getChildWithName (DrawablePathHelpers::fill), mainFill); + needsRedraw = DrawablePathHelpers::updateFillType (tree.getChildWithName (DrawablePathHelpers::stroke), strokeFill) || needsRedraw; - v.setProperty ("colours", s.trimStart(), 0); - } - else + const String jointStyle (tree [DrawablePathHelpers::jointStyle].toString()); + const String endStyle (tree [DrawablePathHelpers::capStyle].toString()); + + PathStrokeType newStroke (tree [DrawablePathHelpers::strokeWidth], + jointStyle == "curved" ? PathStrokeType::curved + : (jointStyle == "bevel" ? PathStrokeType::beveled + : PathStrokeType::mitered), + endStyle == "square" ? PathStrokeType::square + : (endStyle == "round" ? PathStrokeType::rounded + : PathStrokeType::butt)); + + Path newPath; + newPath.restoreFromString (tree [DrawablePathHelpers::path]); + + if (strokeType != newStroke || path != newPath) { - jassertfalse; //xxx + damageRect = getBounds(); + path.swapWithPath (newPath); + strokeType = newStroke; + needsRedraw = true; } - return v; + if (needsRedraw) + damageRect = damageRect.getUnion (getBounds()); + + return damageRect; } -ValueTree DrawablePath::createValueTree() const +const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const { - ValueTree v ("Path"); + ValueTree v (valueTreeType); - v.addChild (createTreeForFillType ("fill", mainFill), -1, 0); - v.addChild (createTreeForFillType ("stroke", strokeFill), -1, 0); + v.addChild (DrawablePathHelpers::createFillType (DrawablePathHelpers::fill, mainFill), -1, 0); + v.addChild (DrawablePathHelpers::createFillType (DrawablePathHelpers::stroke, strokeFill), -1, 0); if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); + v.setProperty (idProperty, getName(), 0); - v.setProperty ("strokeWidth", (double) strokeType.getStrokeThickness(), 0); - v.setProperty ("jointStyle", strokeType.getJointStyle() == PathStrokeType::mitered - ? "miter" : (strokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), 0); - v.setProperty ("capStyle", strokeType.getEndStyle() == PathStrokeType::butt - ? "butt" : (strokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), 0); - v.setProperty ("path", path.toString(), 0); + v.setProperty (DrawablePathHelpers::strokeWidth, (double) strokeType.getStrokeThickness(), 0); + v.setProperty (DrawablePathHelpers::jointStyle, strokeType.getJointStyle() == PathStrokeType::mitered + ? "miter" : (strokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), 0); + v.setProperty (DrawablePathHelpers::capStyle, strokeType.getEndStyle() == PathStrokeType::butt + ? "butt" : (strokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), 0); + v.setProperty (DrawablePathHelpers::path, path.toString(), 0); return v; } -DrawablePath* DrawablePath::createFromValueTree (const ValueTree& tree) -{ - if (! tree.hasType ("Path")) - return 0; - - DrawablePath* p = new DrawablePath(); - - p->setName (tree ["id"]); - p->mainFill = readFillTypeFromTree (tree.getChildWithName ("fill")); - p->strokeFill = readFillTypeFromTree (tree.getChildWithName ("stroke")); - - const String jointStyle (tree ["jointStyle"].toString()); - const String endStyle (tree ["capStyle"].toString()); - - p->strokeType - = PathStrokeType (tree ["strokeWidth"], - jointStyle.equalsIgnoreCase ("curved") ? PathStrokeType::curved - : (jointStyle.equalsIgnoreCase ("bevel") ? PathStrokeType::beveled - : PathStrokeType::mitered), - endStyle.equalsIgnoreCase ("square") ? PathStrokeType::square - : (endStyle.equalsIgnoreCase ("round") ? PathStrokeType::rounded - : PathStrokeType::butt)); - - p->path.clear(); - p->path.restoreFromString (tree ["path"]); - p->updateOutline(); - - return p; -} - END_JUCE_NAMESPACE /*** End of inlined file: juce_DrawablePath.cpp ***/ @@ -84456,31 +84635,29 @@ Drawable* DrawableText::createCopy() const return dt; } -ValueTree DrawableText::createValueTree() const +const Identifier DrawableText::valueTreeType ("Text"); + +const Rectangle DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - ValueTree v ("Text"); + jassert (tree.hasType (valueTreeType)); + setName (tree [idProperty]); + + jassertfalse; // xxx not finished! + + return Rectangle(); +} + +const ValueTree DrawableText::createValueTree (ImageProvider* imageProvider) const +{ + ValueTree v (valueTreeType); if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); + v.setProperty (idProperty, getName(), 0); jassertfalse; // xxx not finished! return v; } -DrawableText* DrawableText::createFromValueTree (const ValueTree& tree) -{ - if (! tree.hasType ("Text")) - return 0; - - DrawableText* dt = new DrawableText(); - - dt->setName (tree ["id"]); - - jassertfalse; // xxx not finished! - - return dt; -} - END_JUCE_NAMESPACE /*** End of inlined file: juce_DrawableText.cpp ***/ @@ -86923,24 +87100,13 @@ const Rectangle GlyphArrangement::getBoundingBox (int startIndex, int num num = glyphs.size() - startIndex; Rectangle result; - bool isFirst = true; while (--num >= 0) { const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); if (includeWhitespace || ! pg->isWhitespace()) - { - if (isFirst) - { - isFirst = false; - result = pg->getBounds(); - } - else - { - result = result.getUnion (pg->getBounds()); - } - } + result = result.getUnion (pg->getBounds()); } return result; @@ -88005,6 +88171,14 @@ bool AffineTransform::isSingularity() const throw() return (mat00 * mat11 - mat10 * mat01) == 0.0; } +const AffineTransform AffineTransform::fromTargetPoints (const float x00, const float y00, + const float x10, const float y10, + const float x01, const float y01) throw() +{ + return AffineTransform (x10 - x00, x01 - x00, x00, + y10 - y00, y01 - y00, y00); +} + bool AffineTransform::isOnlyTranslation() const throw() { return (mat01 == 0) @@ -88250,6 +88424,23 @@ Path& Path::operator= (const Path& other) return *this; } +bool Path::operator== (const Path& other) const throw() +{ + return ! operator!= (other); +} + +bool Path::operator!= (const Path& other) const throw() +{ + if (numElements != other.numElements || useNonZeroWinding != other.useNonZeroWinding) + return true; + + for (int i = 0; i < numElements; ++i) + if (data.elements[i] != other.data.elements[i]) + return true; + + return false; +} + void Path::clear() throw() { numElements = 0; @@ -88259,7 +88450,7 @@ void Path::clear() throw() pathXMax = 0; } -void Path::swapWithPath (Path& other) +void Path::swapWithPath (Path& other) throw() { data.swapWith (other.data); swapVariables (numElements, other.numElements); @@ -252270,17 +252461,6 @@ private: } }; -struct MessageThreadFuncCall -{ - enum { uniqueID = 0x73774623 }; - - MessageCallbackFunction* func; - void* parameter; - void* result; - CriticalSection lock; - WaitableEvent event; -}; - static InternalMessageQueue* juce_internalMessageQueue = 0; // error handling in X11 @@ -252467,6 +252647,17 @@ void MessageManager::broadcastMessage (const String& value) throw() /* TODO */ } +struct MessageThreadFuncCall +{ + enum { uniqueID = 0x73774623 }; + + MessageCallbackFunction* func; + void* parameter; + void* result; + CriticalSection lock; + WaitableEvent event; +}; + void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) { @@ -263538,81 +263729,22 @@ struct CallbackMessagePayload }; END_JUCE_NAMESPACE -using namespace JUCE_NAMESPACE; -@interface JuceAppDelegate : NSObject +@interface JuceCustomMessageHandler : NSObject { } -- (JuceAppDelegate*) init; -- (void) dealloc; -- (BOOL) application: (UIApplication*) application handleOpenURL: (NSURL*) url; -- (void) applicationDidBecomeActive: (NSNotification*) aNotification; -- (void) applicationDidResignActive: (NSNotification*) aNotification; -- (void) applicationWillUnhide: (NSNotification*) aNotification; -- (void) customEvent: (id) data; - (void) performCallback: (id) info; + @end -@implementation JuceAppDelegate - -- (JuceAppDelegate*) init -{ - [super init]; - - [[UIApplication sharedApplication] setDelegate: self]; - - return self; -} - -- (void) dealloc -{ - [[UIApplication sharedApplication] setDelegate: nil]; - [super dealloc]; -} - -- (BOOL) application: (UIApplication*) application handleOpenURL: (NSURL*) url -{ - if (JUCEApplication::getInstance() != 0) - { - JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce ([url absoluteString])); - return YES; - } - - return NO; -} - -- (void) applicationDidBecomeActive: (NSNotification*) aNotification -{ - juce_HandleProcessFocusChange(); -} - -- (void) applicationDidResignActive: (NSNotification*) aNotification -{ - juce_HandleProcessFocusChange(); -} - -- (void) applicationWillUnhide: (NSNotification*) aNotification -{ - juce_HandleProcessFocusChange(); -} - -- (void) customEvent: (id) n -{ - NSData* data = (NSData*) n; - void* message = 0; - [data getBytes: &message length: sizeof (message)]; - [data release]; - - if (message != 0) - MessageManager::getInstance()->deliverMessage (message); -} +@implementation JuceCustomMessageHandler - (void) performCallback: (id) info { if ([info isKindOfClass: [NSData class]]) { - CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes]; + JUCE_NAMESPACE::CallbackMessagePayload* pl = (JUCE_NAMESPACE::CallbackMessagePayload*) [((NSData*) info) bytes]; if (pl != 0) { @@ -263630,8 +263762,6 @@ using namespace JUCE_NAMESPACE; BEGIN_JUCE_NAMESPACE -static JuceAppDelegate* juceAppDelegate = 0; - void MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread @@ -263668,6 +263798,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) static CFRunLoopRef runLoop = 0; static CFRunLoopSourceRef runLoopSource = 0; static Array * pendingMessages = 0; +static JuceCustomMessageHandler* juceCustomMessageHandler = 0; static void runLoopSourceCallback (void*) { @@ -263703,8 +263834,8 @@ void MessageManager::doPlatformSpecificInitialisation() runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - if (juceAppDelegate == 0) - juceAppDelegate = [[JuceAppDelegate alloc] init]; + if (juceCustomMessageHandler == 0) + juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; } void MessageManager::doPlatformSpecificShutdown() @@ -263721,11 +263852,11 @@ void MessageManager::doPlatformSpecificShutdown() deleteAndZero (pendingMessages); } - if (juceAppDelegate != 0) + if (juceCustomMessageHandler != 0) { - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate]; - [juceAppDelegate release]; - juceAppDelegate = 0; + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler]; + [juceCustomMessageHandler release]; + juceCustomMessageHandler = 0; } } @@ -263767,11 +263898,11 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call cmp.result = 0; cmp.hasBeenExecuted = false; - [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) - withObject: [NSData dataWithBytesNoCopy: &cmp - length: sizeof (cmp) - freeWhenDone: NO] - waitUntilDone: YES]; + [juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:) + withObject: [NSData dataWithBytesNoCopy: &cmp + length: sizeof (cmp) + freeWhenDone: NO] + waitUntilDone: YES]; return cmp.result; } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 9b175d4e12..5e416c4f84 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 3 +#define JUCE_BUILDNUMBER 4 /** Current Juce version number. @@ -5759,7 +5759,7 @@ public: The following code is in the header so that the atomics can be inlined where possible... */ #if (JUCE_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 || ! defined (__IPHONE_3_2))) \ - || (JUCE_MAC && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) + || (JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier #if JUCE_PPC || JUCE_IPHONE @@ -12955,7 +12955,7 @@ public: The type is specified when the ValueTree is created. @see hasType */ - const String getType() const; + const Identifier getType() const; /** Returns true if the node has this type. The comparison is case-sensitive. @@ -18856,6 +18856,16 @@ public: */ const AffineTransform inverted() const throw(); + /** Returns the transform that will map three known points onto three coordinates + that are supplied. + + This returns the transform that will transform (0, 0) into (x00, y00), + (1, 0) to (x10, y10), and (0, 1) to (x01, y01). + */ + static const AffineTransform fromTargetPoints (float x00, float y00, + float x10, float y10, + float x01, float y01) throw(); + /** Returns the result of concatenating another transformation after this one. */ const AffineTransform followedBy (const AffineTransform& other) const throw(); @@ -19006,6 +19016,9 @@ public: /** Returns the position of this point, if it is transformed by a given AffineTransform. */ const Point transformedBy (const AffineTransform& transform) const throw() { ValueType x2 (x), y2 (y); transform.transformPoint (x2, y2); return Point (x2, y2); } + /** Casts this point to a Point object. */ + const Point toFloat() const throw() { return Point (static_cast (x), static_cast (y)); } + /** Returns the point as a string in the form "x, y". */ const String toString() const { return String (x) + ", " + String (y); } @@ -20451,11 +20464,16 @@ public: return false; } - /** Returns the smallest rectangle that contains both this one and the one - passed-in. + /** Returns the smallest rectangle that contains both this one and the one passed-in. + + If either this or the other rectangle are empty, they will not be counted as + part of the resulting region. */ const Rectangle getUnion (const Rectangle& other) const throw() { + if (other.isEmpty()) return *this; + if (isEmpty()) return other; + const ValueType newX = jmin (x, other.x); const ValueType newY = jmin (y, other.y); @@ -20839,6 +20857,9 @@ public: /** Copies this path from another one. */ Path& operator= (const Path& other); + bool operator== (const Path& other) const throw(); + bool operator!= (const Path& other) const throw(); + /** Returns true if the path doesn't contain any lines or curves. */ bool isEmpty() const throw(); @@ -21224,7 +21245,7 @@ public: The internal data of the two paths is swapped over, so this is much faster than copying it to a temp variable and back. */ - void swapWithPath (Path& other); + void swapWithPath (Path& other) throw(); /** Applies a 2D transform to all the vertices in the path. @@ -23140,6 +23161,9 @@ public: */ bool isRadial; + bool operator== (const ColourGradient& other) const throw(); + bool operator!= (const ColourGradient& other) const throw(); + juce_UseDebuggingNewOperator private: @@ -23151,6 +23175,9 @@ private: : position (position_), colour (colour_) {} + bool operator== (const ColourPoint& other) const throw() { return position == other.position && colour == other.colour; } + bool operator!= (const ColourPoint& other) const throw() { return position != other.position || colour != other.colour; } + uint32 position; Colour colour; }; @@ -41889,24 +41916,65 @@ public: */ static Drawable* createFromSVG (const XmlElement& svgDocument); + /** This class is used when loading Drawables that contain images, and retrieves + the image for a stored identifier. + @see Drawable::createFromValueTree + */ + class JUCE_API ImageProvider + { + public: + ImageProvider() {} + virtual ~ImageProvider() {} + + /** Retrieves the image associated with this identifier, which could be any + kind of string, number, filename, etc. + + The image that is returned will be owned by the caller, but it may come + from the ImageCache. + */ + virtual Image* getImageForIdentifier (const var& imageIdentifier) = 0; + + /** Returns an identifier to be used to refer to a given image. + This is used when converting a drawable into a ValueTree, so if you're + only loading drawables, you can just return a var::null here. + */ + virtual const var getIdentifierForImage (Image* image) = 0; + }; + /** Tries to create a Drawable from a previously-saved ValueTree. The ValueTree must have been created by the createValueTree() method. + If there are any images used within the drawable, you'll need to provide a valid + ImageProvider object that can be used to retrieve these images from whatever type + of identifier is used to represent them. */ - static Drawable* createFromValueTree (const ValueTree& tree); + static Drawable* createFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + + /** Tries to refresh a Drawable from the same ValueTree that was used to create it. + @returns the damage rectangle that will need repainting due to any changes that were made. + */ + virtual const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) = 0; /** Creates a ValueTree to represent this Drawable. The VarTree that is returned can be turned back into a Drawable with createFromValueTree(). + If there are any images used in this drawable, you'll need to provide a valid + ImageProvider object that can be used to create storable representations of them. */ - virtual ValueTree createValueTree() const = 0; + virtual const ValueTree createValueTree (ImageProvider* imageProvider) const = 0; + + /** Returns the tag ID that is used for a ValueTree that stores this type of drawable. */ + virtual const Identifier getValueTreeType() const = 0; juce_UseDebuggingNewOperator +protected: + static const Identifier idProperty; + private: + String name; + Drawable (const Drawable&); Drawable& operator= (const Drawable&); - - String name; }; #endif // __JUCE_DRAWABLE_JUCEHEADER__ @@ -50657,6 +50725,7 @@ private: Component* panelComponent; int tabDepth; int outlineThickness, edgeIndent; + static const Identifier deleteComponentId; friend class TabCompButtonBar; void changeCallback (int newCurrentTabIndex, const String& newTabName); @@ -57436,16 +57505,12 @@ public: @param drawable the object to add - this will be deleted automatically when no longer needed, so the caller mustn't keep any pointers to it. - @param transform the transform to apply to this drawable when it's being - drawn @param index where to insert it in the list of drawables. 0 is the back, -1 is the front, or any value from 0 and getNumDrawables() can be used @see removeDrawable */ - void insertDrawable (Drawable* drawable, - const AffineTransform& transform = AffineTransform::identity, - int index = -1); + void insertDrawable (Drawable* drawable, int index = -1); /** Adds a new sub-drawable to this one. @@ -57454,16 +57519,12 @@ public: pointer instead. @param drawable the object to add - an internal copy will be made of this object - @param transform the transform to apply to this drawable when it's being - drawn @param index where to insert it in the list of drawables. 0 is the back, -1 is the front, or any value from 0 and getNumDrawables() can be used @see removeDrawable */ - void insertDrawable (const Drawable& drawable, - const AffineTransform& transform = AffineTransform::identity, - int index = -1); + void insertDrawable (const Drawable& drawable, int index = -1); /** Deletes one of the Drawable objects. @@ -57493,15 +57554,6 @@ public: */ Drawable* getDrawable (int index) const throw() { return drawables [index]; } - /** Returns the transform that applies to one of the drawables that are contained in this one. - - The pointer returned is managed by this object and will be deleted when no longer - needed, so be careful what you do with it. - - @see getNumDrawables - */ - const AffineTransform* getDrawableTransform (int index) const throw() { return transforms [index]; } - /** Brings one of the Drawables to the front. @param index the index of the drawable to move, between 0 @@ -57510,6 +57562,38 @@ public: */ void bringToFront (int index); + /** Sets the transform to be applied to this drawable, by defining the positions + where three anchor points should end up in the target rendering space. + + @param targetPositionForOrigin the position that the local coordinate (0, 0) should be + mapped onto when rendering this object. + @param targetPositionForX1Y0 the position that the local coordinate (1, 0) should be + mapped onto when rendering this object. + @param targetPositionForX0Y1 the position that the local coordinate (0, 1) should be + mapped onto when rendering this object. + */ + void setTransform (const Point& targetPositionForOrigin, + const Point& targetPositionForX1Y0, + const Point& targetPositionForX0Y1); + + /** Returns the position to which the local coordinate (0, 0) should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForOrigin() const throw() { return controlPoints[0]; } + + /** Returns the position to which the local coordinate (1, 0) should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForX1Y0() const throw() { return controlPoints[1]; } + + /** Returns the position to which the local coordinate (0, 1) should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForX0Y1() const throw() { return controlPoints[2]; } + /** @internal */ void render (const Drawable::RenderingContext& context) const; /** @internal */ @@ -57517,17 +57601,28 @@ public: /** @internal */ bool hitTest (float x, float y) const; /** @internal */ + int getNumControlPoints() const; + /** @internal */ + const Point getControlPoint (int index) const; + /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawableComposite* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } juce_UseDebuggingNewOperator private: OwnedArray drawables; - OwnedArray transforms; + Point controlPoints[3]; + + const Rectangle getUntransformedBounds() const; + const AffineTransform getTransform() const; DrawableComposite (const DrawableComposite&); DrawableComposite& operator= (const DrawableComposite&); @@ -57568,9 +57663,6 @@ public: /** Sets the image that this drawable will render. - An internal copy of this will not be made, so the caller mustn't delete - the image while it's still being used by this object. - A good way to use this is with the ImageCache - if you create an image with ImageCache and pass it in here with releaseWhenNotNeeded = true, then it'll be released neatly with its reference count being decreased. @@ -57581,8 +57673,7 @@ public: needs it - unless the image was created by the ImageCache, in which case it will be released with ImageCache::release(). */ - void setImage (Image* imageToUse, - bool releaseWhenNotNeeded); + void setImage (Image* imageToUse, bool releaseWhenNotNeeded); /** Returns the current image. */ Image* getImage() const throw() { return image; } @@ -57610,6 +57701,38 @@ public: /** Returns the overlay colour. */ const Colour& getOverlayColour() const throw() { return overlayColour; } + /** Sets the transform to be applied to this image, by defining the positions + where three anchor points should end up in the target rendering space. + + @param imageTopLeftPosition the position that the image's top-left corner should be mapped to + in the target coordinate space. + @param imageTopRightPosition the position that the image's top-right corner should be mapped to + in the target coordinate space. + @param imageBottomLeftPosition the position that the image's bottom-left corner should be mapped to + in the target coordinate space. + */ + void setTransform (const Point& imageTopLeftPosition, + const Point& imageTopRightPosition, + const Point& imageBottomLeftPosition); + + /** Returns the position to which the image's top-left corner should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForTopLeft() const throw() { return controlPoints[0]; } + + /** Returns the position to which the image's top-right corner should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForTopRight() const throw() { return controlPoints[1]; } + + /** Returns the position to which the image's bottom-left corner should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForBottomLeft() const throw() { return controlPoints[2]; } + /** @internal */ void render (const Drawable::RenderingContext& context) const; /** @internal */ @@ -57619,9 +57742,13 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawableImage* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } juce_UseDebuggingNewOperator @@ -57630,6 +57757,9 @@ private: bool canDeleteImage; float opacity; Colour overlayColour; + Point controlPoints[3]; + + const AffineTransform getTransform() const; DrawableImage (const DrawableImage&); DrawableImage& operator= (const DrawableImage&); @@ -57719,9 +57849,13 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawablePath* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } juce_UseDebuggingNewOperator @@ -57791,9 +57925,13 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawableText* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } juce_UseDebuggingNewOperator diff --git a/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp b/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp index 6f0368e79e..161ff1961d 100644 --- a/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp @@ -88,7 +88,7 @@ public: { bufferList.calloc (256, 1); -#ifdef WIN32 +#if JUCE_WINDOWS if (InitializeQTML (0) != noErr) return; #endif diff --git a/src/containers/juce_ValueTree.cpp b/src/containers/juce_ValueTree.cpp index d340a22a3c..298f568215 100644 --- a/src/containers/juce_ValueTree.cpp +++ b/src/containers/juce_ValueTree.cpp @@ -566,9 +566,9 @@ bool ValueTree::hasType (const Identifier& typeName) const return object != 0 && object->type == typeName; } -const String ValueTree::getType() const +const Identifier ValueTree::getType() const { - return object != 0 ? object->type.toString() : String::empty; + return object != 0 ? object->type : Identifier(); } ValueTree ValueTree::getParent() const @@ -807,7 +807,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) //============================================================================== void ValueTree::writeToStream (OutputStream& output) { - output.writeString (getType()); + output.writeString (getType().toString()); const int numProps = getNumProperties(); output.writeCompressedInt (numProps); diff --git a/src/containers/juce_ValueTree.h b/src/containers/juce_ValueTree.h index 27007a20a0..d486c0f77e 100644 --- a/src/containers/juce_ValueTree.h +++ b/src/containers/juce_ValueTree.h @@ -125,7 +125,7 @@ public: The type is specified when the ValueTree is created. @see hasType */ - const String getType() const; + const Identifier getType() const; /** Returns true if the node has this type. The comparison is case-sensitive. diff --git a/src/core/juce_Atomic.h b/src/core/juce_Atomic.h index bfcbedf33a..96d65385b9 100644 --- a/src/core/juce_Atomic.h +++ b/src/core/juce_Atomic.h @@ -153,7 +153,7 @@ public: The following code is in the header so that the atomics can be inlined where possible... */ #if (JUCE_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 || ! defined (__IPHONE_3_2))) \ - || (JUCE_MAC && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) + || (JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier #if JUCE_PPC || JUCE_IPHONE diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index ec97b1d412..873b577d1f 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 3 +#define JUCE_BUILDNUMBER 4 /** Current Juce version number. diff --git a/src/gui/components/controls/juce_ListBox.cpp b/src/gui/components/controls/juce_ListBox.cpp index 63e3fe9de7..0b87b9c69f 100644 --- a/src/gui/components/controls/juce_ListBox.cpp +++ b/src/gui/components/controls/juce_ListBox.cpp @@ -895,11 +895,7 @@ Image* ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) { const Point pos (rowComp->relativePositionToOtherComponent (this, Point())); const Rectangle rowRect (pos.getX(), pos.getY(), rowComp->getWidth(), rowComp->getHeight()); - - if (imageArea.isEmpty()) - imageArea = rowRect; - else - imageArea = imageArea.getUnion (rowRect); + imageArea = imageArea.getUnion (rowRect); } } diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index bd2f98cbdb..eca866f7da 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -2929,14 +2929,16 @@ bool Component::isFocusContainer() const throw() return flags.isFocusContainerFlag; } +static const Identifier juce_explicitFocusOrderId ("_jexfo"); + int Component::getExplicitFocusOrder() const { - return properties ["_jexfo"]; + return properties [juce_explicitFocusOrderId]; } void Component::setExplicitFocusOrder (const int newFocusOrderIndex) { - properties.set ("_jexfo", newFocusOrderIndex); + properties.set (juce_explicitFocusOrderId, newFocusOrderIndex); } KeyboardFocusTraverser* Component::createFocusTraverser() diff --git a/src/gui/components/layout/juce_TabbedComponent.cpp b/src/gui/components/layout/juce_TabbedComponent.cpp index c4626fc973..85c541851c 100644 --- a/src/gui/components/layout/juce_TabbedComponent.cpp +++ b/src/gui/components/layout/juce_TabbedComponent.cpp @@ -118,6 +118,8 @@ TabBarButton* TabbedComponent::createTabButton (const String& tabName, const int } //============================================================================== +const Identifier TabbedComponent::deleteComponentId ("deleteByTabComp_"); + void TabbedComponent::clearTabs() { if (panelComponent != 0) @@ -136,7 +138,7 @@ void TabbedComponent::clearTabs() // be careful not to delete these components until they've been removed from the tab component jassert (c == 0 || c->isValidComponent()); - if (c != 0 && (bool) c->getProperties() ["deleteByTabComp_"]) + if (c != 0 && (bool) c->getProperties() [deleteComponentId]) delete c; } @@ -152,7 +154,7 @@ void TabbedComponent::addTab (const String& tabName, contentComponents.insert (insertIndex, contentComponent); if (contentComponent != 0) - contentComponent->getProperties().set ("deleteByTabComp_", deleteComponentWhenNotNeeded); + contentComponent->getProperties().set (deleteComponentId, deleteComponentWhenNotNeeded); tabs->addTab (tabName, tabBackgroundColour, insertIndex); } @@ -166,7 +168,7 @@ void TabbedComponent::removeTab (const int tabIndex) { Component* const c = contentComponents [tabIndex]; - if (c != 0 && (bool) c->getProperties() ["deleteByTabComp_"]) + if (c != 0 && (bool) c->getProperties() [deleteComponentId]) { if (c == panelComponent) panelComponent = 0; diff --git a/src/gui/components/layout/juce_TabbedComponent.h b/src/gui/components/layout/juce_TabbedComponent.h index fe777a95b7..007558a301 100644 --- a/src/gui/components/layout/juce_TabbedComponent.h +++ b/src/gui/components/layout/juce_TabbedComponent.h @@ -232,6 +232,7 @@ private: Component* panelComponent; int tabDepth; int outlineThickness, edgeIndent; + static const Identifier deleteComponentId; friend class TabCompButtonBar; void changeCallback (int newCurrentTabIndex, const String& newTabName); diff --git a/src/gui/components/mouse/juce_MouseInputSource.cpp b/src/gui/components/mouse/juce_MouseInputSource.cpp index 1824cd0cbc..ba1fbd1e17 100644 --- a/src/gui/components/mouse/juce_MouseInputSource.cpp +++ b/src/gui/components/mouse/juce_MouseInputSource.cpp @@ -328,8 +328,7 @@ public: { if (mouseDowns[0].time - mouseDowns[i].time < (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))) && abs (mouseDowns[0].position.getX() - mouseDowns[i].position.getX()) < 8 - && abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8 - && mouseDowns[0].component == mouseDowns[i].component) + && abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8) { ++numClicks; } diff --git a/src/gui/graphics/colour/juce_ColourGradient.cpp b/src/gui/graphics/colour/juce_ColourGradient.cpp index f5c53ee47a..b29a9e7cbe 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.cpp +++ b/src/gui/graphics/colour/juce_ColourGradient.cpp @@ -57,6 +57,18 @@ ColourGradient::~ColourGradient() { } +bool ColourGradient::operator== (const ColourGradient& other) const throw() +{ + return point1 == other.point1 && point2 == other.point2 + && isRadial == other.isRadial + && colours == other.colours; +} + +bool ColourGradient::operator!= (const ColourGradient& other) const throw() +{ + return ! operator== (other); +} + //============================================================================== void ColourGradient::clearColours() { diff --git a/src/gui/graphics/colour/juce_ColourGradient.h b/src/gui/graphics/colour/juce_ColourGradient.h index d979884b89..1ab5788cd9 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.h +++ b/src/gui/graphics/colour/juce_ColourGradient.h @@ -141,6 +141,9 @@ public: */ bool isRadial; + bool operator== (const ColourGradient& other) const throw(); + bool operator!= (const ColourGradient& other) const throw(); + //============================================================================== juce_UseDebuggingNewOperator @@ -153,6 +156,9 @@ private: : position (position_), colour (colour_) {} + bool operator== (const ColourPoint& other) const throw() { return position == other.position && colour == other.colour; } + bool operator!= (const ColourPoint& other) const throw() { return position != other.position || colour != other.colour; } + uint32 position; Colour colour; }; diff --git a/src/gui/graphics/drawables/juce_Drawable.cpp b/src/gui/graphics/drawables/juce_Drawable.cpp index 6f86857893..db551443b5 100644 --- a/src/gui/graphics/drawables/juce_Drawable.cpp +++ b/src/gui/graphics/drawables/juce_Drawable.cpp @@ -36,6 +36,8 @@ BEGIN_JUCE_NAMESPACE #include "../../../text/juce_XmlDocument.h" #include "../../../io/files/juce_FileInputStream.h" +const Identifier Drawable::idProperty ("id"); + //============================================================================== Drawable::RenderingContext::RenderingContext (Graphics& g_, const AffineTransform& transform_, @@ -133,22 +135,22 @@ Drawable* Drawable::createFromImageFile (const File& file) } //============================================================================== -Drawable* Drawable::createFromValueTree (const ValueTree& tree) +Drawable* Drawable::createFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - Drawable* d = DrawablePath::createFromValueTree (tree); + const Identifier type (tree.getType()); - if (d == 0) - { - d = DrawableComposite::createFromValueTree (tree); + Drawable* d = 0; + if (type == DrawablePath::valueTreeType) + d = new DrawablePath(); + else if (type == DrawableComposite::valueTreeType) + d = new DrawableComposite(); + else if (type == DrawableImage::valueTreeType) + d = new DrawableImage(); + else if (type == DrawableText::valueTreeType) + d = new DrawableText(); - if (d == 0) - { - d = DrawableImage::createFromValueTree (tree); - - if (d == 0) - d = DrawableText::createFromValueTree (tree); - } - } + if (d != 0) + d->refreshFromValueTree (tree, imageProvider); return d; } diff --git a/src/gui/graphics/drawables/juce_Drawable.h b/src/gui/graphics/drawables/juce_Drawable.h index 1f21069e3d..e8469b9ca0 100644 --- a/src/gui/graphics/drawables/juce_Drawable.h +++ b/src/gui/graphics/drawables/juce_Drawable.h @@ -180,25 +180,66 @@ public: static Drawable* createFromSVG (const XmlElement& svgDocument); //============================================================================== + /** This class is used when loading Drawables that contain images, and retrieves + the image for a stored identifier. + @see Drawable::createFromValueTree + */ + class JUCE_API ImageProvider + { + public: + ImageProvider() {} + virtual ~ImageProvider() {} + + /** Retrieves the image associated with this identifier, which could be any + kind of string, number, filename, etc. + + The image that is returned will be owned by the caller, but it may come + from the ImageCache. + */ + virtual Image* getImageForIdentifier (const var& imageIdentifier) = 0; + + /** Returns an identifier to be used to refer to a given image. + This is used when converting a drawable into a ValueTree, so if you're + only loading drawables, you can just return a var::null here. + */ + virtual const var getIdentifierForImage (Image* image) = 0; + }; + /** Tries to create a Drawable from a previously-saved ValueTree. The ValueTree must have been created by the createValueTree() method. + If there are any images used within the drawable, you'll need to provide a valid + ImageProvider object that can be used to retrieve these images from whatever type + of identifier is used to represent them. */ - static Drawable* createFromValueTree (const ValueTree& tree); + static Drawable* createFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); + + /** Tries to refresh a Drawable from the same ValueTree that was used to create it. + @returns the damage rectangle that will need repainting due to any changes that were made. + */ + virtual const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) = 0; /** Creates a ValueTree to represent this Drawable. The VarTree that is returned can be turned back into a Drawable with createFromValueTree(). + If there are any images used in this drawable, you'll need to provide a valid + ImageProvider object that can be used to create storable representations of them. */ - virtual ValueTree createValueTree() const = 0; + virtual const ValueTree createValueTree (ImageProvider* imageProvider) const = 0; + + /** Returns the tag ID that is used for a ValueTree that stores this type of drawable. */ + virtual const Identifier getValueTreeType() const = 0; //============================================================================== juce_UseDebuggingNewOperator +protected: + static const Identifier idProperty; + private: + String name; + Drawable (const Drawable&); Drawable& operator= (const Drawable&); - - String name; }; diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.cpp b/src/gui/graphics/drawables/juce_DrawableComposite.cpp index f3077ac5d6..433d7f5236 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.cpp +++ b/src/gui/graphics/drawables/juce_DrawableComposite.cpp @@ -37,6 +37,8 @@ BEGIN_JUCE_NAMESPACE //============================================================================== DrawableComposite::DrawableComposite() { + controlPoints[1].setXY (1.0f, 0.0f); + controlPoints[2].setXY (0.0f, 1.0f); } DrawableComposite::~DrawableComposite() @@ -44,51 +46,48 @@ DrawableComposite::~DrawableComposite() } //============================================================================== -void DrawableComposite::insertDrawable (Drawable* drawable, - const AffineTransform& transform, - const int index) +void DrawableComposite::insertDrawable (Drawable* drawable, const int index) { if (drawable != 0) { - if (! drawables.contains (drawable)) - { - drawables.insert (index, drawable); - - if (transform.isIdentity()) - transforms.insert (index, 0); - else - transforms.insert (index, new AffineTransform (transform)); - } - else - { - jassertfalse; // trying to add a drawable that's already in here! - } + jassert (! drawables.contains (drawable)); // trying to add a drawable that's already in here! + drawables.insert (index, drawable); } } -void DrawableComposite::insertDrawable (const Drawable& drawable, - const AffineTransform& transform, - const int index) +void DrawableComposite::insertDrawable (const Drawable& drawable, const int index) { - insertDrawable (drawable.createCopy(), transform, index); + insertDrawable (drawable.createCopy(), index); } void DrawableComposite::removeDrawable (const int index, const bool deleteDrawable) { drawables.remove (index, deleteDrawable); - transforms.remove (index); } void DrawableComposite::bringToFront (const int index) { if (index >= 0 && index < drawables.size() - 1) - { drawables.move (index, -1); - transforms.move (index, -1); - } +} + +void DrawableComposite::setTransform (const Point& targetPositionForOrigin, + const Point& targetPositionForX1Y0, + const Point& targetPositionForX0Y1) +{ + controlPoints[0] = targetPositionForOrigin; + controlPoints[1] = targetPositionForX1Y0; + controlPoints[2] = targetPositionForX0Y1; } //============================================================================== +const AffineTransform DrawableComposite::getTransform() const +{ + return AffineTransform::fromTargetPoints (controlPoints[0].getX(), controlPoints[0].getY(), + controlPoints[1].getX(), controlPoints[1].getY(), + controlPoints[2].getX(), controlPoints[2].getY()); +} + void DrawableComposite::render (const Drawable::RenderingContext& context) const { if (drawables.size() > 0 && context.opacity > 0) @@ -96,15 +95,10 @@ void DrawableComposite::render (const Drawable::RenderingContext& context) const if (context.opacity >= 1.0f || drawables.size() == 1) { Drawable::RenderingContext contextCopy (context); + contextCopy.transform = getTransform().followedBy (context.transform); for (int i = 0; i < drawables.size(); ++i) - { - const AffineTransform* const t = transforms.getUnchecked(i); - contextCopy.transform = (t == 0) ? context.transform - : t->followedBy (context.transform); - drawables.getUnchecked(i)->render (contextCopy); - } } else { @@ -127,42 +121,28 @@ void DrawableComposite::render (const Drawable::RenderingContext& context) const } } -const Rectangle DrawableComposite::getBounds() const +const Rectangle DrawableComposite::getUntransformedBounds() const { Rectangle bounds; for (int i = 0; i < drawables.size(); ++i) - { - const Drawable* const d = drawables.getUnchecked(i); - const AffineTransform* const t = transforms.getUnchecked(i); - - const Rectangle childBounds (t == 0 ? d->getBounds() - : d->getBounds().transformed (*t)); - - if (bounds.isEmpty()) - bounds = childBounds; - else if (! childBounds.isEmpty()) - bounds = bounds.getUnion (childBounds); - } + bounds = bounds.getUnion (drawables.getUnchecked(i)->getBounds()); return bounds; } +const Rectangle DrawableComposite::getBounds() const +{ + return getUntransformedBounds().transformed (getTransform()); +} + bool DrawableComposite::hitTest (float x, float y) const { + getTransform().inverted().transformPoint (x, y); + for (int i = 0; i < drawables.size(); ++i) - { - float tx = x; - float ty = y; - - const AffineTransform* const t = transforms.getUnchecked(i); - - if (t != 0) - t->inverted().transformPoint (tx, ty); - - if (drawables.getUnchecked(i)->hitTest (tx, ty)) + if (drawables.getUnchecked(i)->hitTest (x, y)) return true; - } return false; } @@ -171,88 +151,124 @@ Drawable* DrawableComposite::createCopy() const { DrawableComposite* const dc = new DrawableComposite(); - for (int i = 0; i < drawables.size(); ++i) - { - dc->drawables.add (drawables.getUnchecked(i)->createCopy()); + for (int i = 0; i < 3; ++i) + dc->controlPoints[i] = controlPoints[i]; - const AffineTransform* const t = transforms.getUnchecked(i); - dc->transforms.add (t != 0 ? new AffineTransform (*t) : 0); - } + for (int i = 0; i < drawables.size(); ++i) + dc->drawables.add (drawables.getUnchecked(i)->createCopy()); return dc; } //============================================================================== -ValueTree DrawableComposite::createValueTree() const +const Identifier DrawableComposite::valueTreeType ("Group"); + +namespace DrawableCompositeHelpers { - ValueTree v ("Group"); + static const Identifier topLeft ("topLeft"); + static const Identifier topRight ("topRight"); + static const Identifier bottomLeft ("bottomLeft"); + + static void stringToPoint (const String& coords, Point& point) + { + if (coords.isNotEmpty()) + { + const int comma = coords.indexOfChar (','); + point.setXY (coords.substring (0, comma).getFloatValue(), + coords.substring (comma).getFloatValue()); + } + } + + static const var pointToString (const Point& point) + { + return String (point.getX()) + ", " + String (point.getY()); + } +} + +const Rectangle DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) +{ + jassert (tree.hasType (valueTreeType)); + + Rectangle damageRect; + + setName (tree [idProperty]); + + Point newControlPoint[3]; + DrawableCompositeHelpers::stringToPoint (tree [DrawableCompositeHelpers::topLeft].toString(), newControlPoint[0]); + DrawableCompositeHelpers::stringToPoint (tree [DrawableCompositeHelpers::topRight].toString(), newControlPoint[1]); + DrawableCompositeHelpers::stringToPoint (tree [DrawableCompositeHelpers::bottomLeft].toString(), newControlPoint[2]); + bool controlPointsChanged = false; + + if (controlPoints[0] != newControlPoint[0] + || controlPoints[1] != newControlPoint[1] + || controlPoints[2] != newControlPoint[2]) + { + controlPointsChanged = true; + damageRect = getUntransformedBounds(); + controlPoints[0] = newControlPoint[0]; + controlPoints[1] = newControlPoint[1]; + controlPoints[2] = newControlPoint[2]; + } + + int i; + for (i = drawables.size(); --i >= tree.getNumChildren();) + { + damageRect = damageRect.getUnion (drawables.getUnchecked(i)->getBounds()); + drawables.remove (i); + } + + for (int i = 0; i < tree.getNumChildren(); ++i) + { + const ValueTree childTree (tree.getChild (i)); + Drawable* d = drawables[i]; + + if (d != 0) + { + if (childTree.hasType (d->getValueTreeType())) + { + damageRect = damageRect.getUnion (d->refreshFromValueTree (childTree, imageProvider)); + } + else + { + damageRect = damageRect.getUnion (d->getBounds()); + d = createFromValueTree (childTree, imageProvider); + drawables.set (i, d); + damageRect = damageRect.getUnion (d->getBounds()); + } + } + else + { + d = createFromValueTree (childTree, imageProvider); + drawables.set (i, d); + damageRect = damageRect.getUnion (d->getBounds()); + } + } + + if (controlPointsChanged) + damageRect = damageRect.getUnion (getUntransformedBounds()); + + return damageRect.transformed (getTransform()); +} + +const ValueTree DrawableComposite::createValueTree (ImageProvider* imageProvider) const +{ + ValueTree v (valueTreeType); if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); + v.setProperty (idProperty, getName(), 0); + + if (! getTransform().isIdentity()) + { + v.setProperty (DrawableCompositeHelpers::topLeft, DrawableCompositeHelpers::pointToString (controlPoints[0]), 0); + v.setProperty (DrawableCompositeHelpers::topRight, DrawableCompositeHelpers::pointToString (controlPoints[1]), 0); + v.setProperty (DrawableCompositeHelpers::bottomLeft, DrawableCompositeHelpers::pointToString (controlPoints[2]), 0); + } for (int i = 0; i < drawables.size(); ++i) - { - Drawable* const d = drawables.getUnchecked(i); - - ValueTree child (d->createValueTree()); - - AffineTransform* transform = transforms.getUnchecked(i); - - if (transform != 0) - { - String t; - t << transform->mat00 << " " << transform->mat01 << " " << transform->mat02 << " " - << transform->mat10 << " " << transform->mat11 << " " << transform->mat12; - - child.setProperty ("transform", t, 0); - } - - v.addChild (child, -1, 0); - } + v.addChild (drawables.getUnchecked(i)->createValueTree (imageProvider), -1, 0); return v; } -DrawableComposite* DrawableComposite::createFromValueTree (const ValueTree& tree) -{ - if (! tree.hasType ("Group")) - return 0; - - DrawableComposite* dc = new DrawableComposite(); - dc->setName (tree ["id"]); - - for (int i = 0; i < tree.getNumChildren(); ++i) - { - ValueTree childTree (tree.getChild (i)); - Drawable* d = Drawable::createFromValueTree (childTree); - - if (d != 0) - { - AffineTransform transform; - const String transformAtt (childTree ["transform"].toString()); - - if (transformAtt.isNotEmpty()) - { - StringArray tokens; - tokens.addTokens (transformAtt.trim(), false); - tokens.removeEmptyStrings (true); - - if (tokens.size() == 6) - { - float f[6]; - for (int j = 0; j < 6; ++j) - f[j] = (float) tokens[j].getDoubleValue(); - - transform = AffineTransform (f[0], f[1], f[2], f[3], f[4], f[5]); - } - } - - dc->insertDrawable (d, transform); - } - } - - return dc; -} - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/drawables/juce_DrawableComposite.h b/src/gui/graphics/drawables/juce_DrawableComposite.h index aff05168cb..3fb70925c3 100644 --- a/src/gui/graphics/drawables/juce_DrawableComposite.h +++ b/src/gui/graphics/drawables/juce_DrawableComposite.h @@ -55,16 +55,12 @@ public: @param drawable the object to add - this will be deleted automatically when no longer needed, so the caller mustn't keep any pointers to it. - @param transform the transform to apply to this drawable when it's being - drawn @param index where to insert it in the list of drawables. 0 is the back, -1 is the front, or any value from 0 and getNumDrawables() can be used @see removeDrawable */ - void insertDrawable (Drawable* drawable, - const AffineTransform& transform = AffineTransform::identity, - int index = -1); + void insertDrawable (Drawable* drawable, int index = -1); /** Adds a new sub-drawable to this one. @@ -73,16 +69,12 @@ public: pointer instead. @param drawable the object to add - an internal copy will be made of this object - @param transform the transform to apply to this drawable when it's being - drawn @param index where to insert it in the list of drawables. 0 is the back, -1 is the front, or any value from 0 and getNumDrawables() can be used @see removeDrawable */ - void insertDrawable (const Drawable& drawable, - const AffineTransform& transform = AffineTransform::identity, - int index = -1); + void insertDrawable (const Drawable& drawable, int index = -1); /** Deletes one of the Drawable objects. @@ -112,15 +104,6 @@ public: */ Drawable* getDrawable (int index) const throw() { return drawables [index]; } - /** Returns the transform that applies to one of the drawables that are contained in this one. - - The pointer returned is managed by this object and will be deleted when no longer - needed, so be careful what you do with it. - - @see getNumDrawables - */ - const AffineTransform* getDrawableTransform (int index) const throw() { return transforms [index]; } - /** Brings one of the Drawables to the front. @param index the index of the drawable to move, between 0 @@ -129,6 +112,37 @@ public: */ void bringToFront (int index); + /** Sets the transform to be applied to this drawable, by defining the positions + where three anchor points should end up in the target rendering space. + + @param targetPositionForOrigin the position that the local coordinate (0, 0) should be + mapped onto when rendering this object. + @param targetPositionForX1Y0 the position that the local coordinate (1, 0) should be + mapped onto when rendering this object. + @param targetPositionForX0Y1 the position that the local coordinate (0, 1) should be + mapped onto when rendering this object. + */ + void setTransform (const Point& targetPositionForOrigin, + const Point& targetPositionForX1Y0, + const Point& targetPositionForX0Y1); + + /** Returns the position to which the local coordinate (0, 0) should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForOrigin() const throw() { return controlPoints[0]; } + + /** Returns the position to which the local coordinate (1, 0) should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForX1Y0() const throw() { return controlPoints[1]; } + + /** Returns the position to which the local coordinate (0, 1) should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForX0Y1() const throw() { return controlPoints[2]; } //============================================================================== /** @internal */ @@ -138,18 +152,29 @@ public: /** @internal */ bool hitTest (float x, float y) const; /** @internal */ + int getNumControlPoints() const; + /** @internal */ + const Point getControlPoint (int index) const; + /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawableComposite* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } //============================================================================== juce_UseDebuggingNewOperator private: OwnedArray drawables; - OwnedArray transforms; + Point controlPoints[3]; + + const Rectangle getUntransformedBounds() const; + const AffineTransform getTransform() const; DrawableComposite (const DrawableComposite&); DrawableComposite& operator= (const DrawableComposite&); diff --git a/src/gui/graphics/drawables/juce_DrawableImage.cpp b/src/gui/graphics/drawables/juce_DrawableImage.cpp index be456fc09b..7e941ab75d 100644 --- a/src/gui/graphics/drawables/juce_DrawableImage.cpp +++ b/src/gui/graphics/drawables/juce_DrawableImage.cpp @@ -30,8 +30,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_DrawableImage.h" #include "../imaging/juce_ImageCache.h" -#include "../imaging/juce_ImageFileFormat.h" -#include "../../../io/streams/juce_MemoryOutputStream.h" + //============================================================================== DrawableImage::DrawableImage() @@ -40,6 +39,8 @@ DrawableImage::DrawableImage() opacity (1.0f), overlayColour (0x00000000) { + controlPoints[1].setXY (1.0f, 0.0f); + controlPoints[2].setXY (0.0f, 1.0f); } DrawableImage::~DrawableImage() @@ -61,6 +62,10 @@ void DrawableImage::setImage (const Image& imageToCopy) clearImage(); image = new Image (imageToCopy); canDeleteImage = true; + + controlPoints[0].setXY (0.0f, 0.0f); + controlPoints[1].setXY ((float) image->getWidth(), 0.0f); + controlPoints[2].setXY (0.0f, (float) image->getHeight()); } void DrawableImage::setImage (Image* imageToUse, @@ -69,6 +74,13 @@ void DrawableImage::setImage (Image* imageToUse, clearImage(); image = imageToUse; canDeleteImage = releaseWhenNotNeeded; + + if (image != 0) + { + controlPoints[0].setXY (0.0f, 0.0f); + controlPoints[1].setXY ((float) image->getWidth(), 0.0f); + controlPoints[2].setXY (0.0f, (float) image->getHeight()); + } } void DrawableImage::setOpacity (const float newOpacity) @@ -81,23 +93,45 @@ void DrawableImage::setOverlayColour (const Colour& newOverlayColour) overlayColour = newOverlayColour; } +void DrawableImage::setTransform (const Point& imageTopLeftPosition, + const Point& imageTopRightPosition, + const Point& imageBottomLeftPosition) +{ + controlPoints[0] = imageTopLeftPosition; + controlPoints[1] = imageTopRightPosition; + controlPoints[2] = imageBottomLeftPosition; +} + //============================================================================== +const AffineTransform DrawableImage::getTransform() const +{ + if (image == 0) + return AffineTransform::identity; + + const Point tr (controlPoints[0] + (controlPoints[1] - controlPoints[0]) / image->getWidth()); + const Point bl (controlPoints[0] + (controlPoints[2] - controlPoints[0]) / image->getHeight()); + + return AffineTransform::fromTargetPoints (controlPoints[0].getX(), controlPoints[0].getY(), + tr.getX(), tr.getY(), + bl.getX(), bl.getY()); +} + void DrawableImage::render (const Drawable::RenderingContext& context) const { if (image != 0) { + const AffineTransform t (getTransform().followedBy (context.transform)); + if (opacity > 0.0f && ! overlayColour.isOpaque()) { context.g.setOpacity (context.opacity * opacity); - context.g.drawImageTransformed (image, image->getBounds(), - context.transform, false); + context.g.drawImageTransformed (image, image->getBounds(), t, false); } if (! overlayColour.isTransparent()) { context.g.setColour (overlayColour.withMultipliedAlpha (context.opacity)); - context.g.drawImageTransformed (image, image->getBounds(), - context.transform, true); + context.g.drawImageTransformed (image, image->getBounds(), t, true); } } } @@ -107,17 +141,38 @@ const Rectangle DrawableImage::getBounds() const if (image == 0) return Rectangle(); - return Rectangle (0, 0, (float) image->getWidth(), (float) image->getHeight()); + const Point bottomRight (controlPoints[1] + (controlPoints[2] - controlPoints[0])); + float minX = bottomRight.getX(); + float maxX = minX; + float minY = bottomRight.getY(); + float maxY = minY; + + for (int i = 0; i < 3; ++i) + { + minX = jmin (minX, controlPoints[i].getX()); + maxX = jmax (maxX, controlPoints[i].getX()); + minY = jmin (minY, controlPoints[i].getY()); + maxY = jmax (maxY, controlPoints[i].getY()); + } + + return Rectangle (minX, minY, maxX - minX, maxY - minY); } bool DrawableImage::hitTest (float x, float y) const { - return image != 0 - && x >= 0.0f - && y >= 0.0f - && x < image->getWidth() - && y < image->getHeight() - && image->getPixelAt (roundToInt (x), roundToInt (y)).getAlpha() >= 127; + if (image == 0) + return false; + + getTransform().inverted().transformPoint (x, y); + + const int ix = roundToInt (x); + const int iy = roundToInt (y); + + return ix >= 0 + && iy >= 0 + && ix < image->getWidth() + && iy < image->getHeight() + && image->getPixelAt (ix, iy).getAlpha() >= 127; } Drawable* DrawableImage::createCopy() const @@ -127,6 +182,9 @@ Drawable* DrawableImage::createCopy() const di->opacity = opacity; di->overlayColour = overlayColour; + for (int i = 0; i < 4; ++i) + di->controlPoints[i] = controlPoints[i]; + if (image != 0) { if ((! canDeleteImage) || ! ImageCache::isImageInCache (image)) @@ -144,60 +202,111 @@ Drawable* DrawableImage::createCopy() const } //============================================================================== -ValueTree DrawableImage::createValueTree() const +const Identifier DrawableImage::valueTreeType ("Image"); + +namespace DrawableImageHelpers { - ValueTree v ("Image"); + static const Identifier opacity ("opacity"); + static const Identifier overlay ("overlay"); + static const Identifier image ("image"); + static const Identifier topLeft ("topLeft"); + static const Identifier topRight ("topRight"); + static const Identifier bottomLeft ("bottomLeft"); + + static void stringToPoint (const String& coords, Point& point) + { + if (coords.isNotEmpty()) + { + const int comma = coords.indexOfChar (','); + point.setXY (coords.substring (0, comma).getFloatValue(), + coords.substring (comma).getFloatValue()); + } + } + + static const var pointToString (const Point& point) + { + return String (point.getX()) + ", " + String (point.getY()); + } +} + +const Rectangle DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) +{ + jassert (tree.hasType (valueTreeType)); + + setName (tree [idProperty]); + + const float newOpacity = tree.getProperty (DrawableImageHelpers::opacity, 1.0); + const Colour newOverlayColour (tree [DrawableImageHelpers::overlay].toString().getHexValue32()); + + Image* newImage = 0; + const String imageIdentifier (tree [DrawableImageHelpers::image].toString()); + if (imageIdentifier.isNotEmpty()) + { + jassert (imageProvider != 0); // if you're using images, you need to provide something that can load and save them! + + if (imageProvider != 0) + newImage = imageProvider->getImageForIdentifier (imageIdentifier); + } + + Point newControlPoint[3]; + DrawableImageHelpers::stringToPoint (tree [DrawableImageHelpers::topLeft].toString(), newControlPoint[0]); + DrawableImageHelpers::stringToPoint (tree [DrawableImageHelpers::topRight].toString(), newControlPoint[1]); + DrawableImageHelpers::stringToPoint (tree [DrawableImageHelpers::bottomLeft].toString(), newControlPoint[2]); + + if (newOpacity != opacity || overlayColour != newOverlayColour || image != newImage + || controlPoints[0] != newControlPoint[0] + || controlPoints[1] != newControlPoint[1] + || controlPoints[2] != newControlPoint[2]) + { + opacity = newOpacity; + overlayColour = newOverlayColour; + controlPoints[0] = newControlPoint[0]; + controlPoints[1] = newControlPoint[1]; + controlPoints[2] = newControlPoint[2]; + + if (image != newImage) + { + ImageCache::release (image); + image = newImage; + } + + return getBounds(); + } + + ImageCache::release (newImage); + return Rectangle(); +} + +const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const +{ + ValueTree v (valueTreeType); if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); + v.setProperty (idProperty, getName(), 0); if (opacity < 1.0f) - v.setProperty ("opacity", (double) opacity, 0); + v.setProperty (DrawableImageHelpers::opacity, (double) opacity, 0); if (! overlayColour.isTransparent()) - v.setProperty ("overlay", String::toHexString ((int) overlayColour.getARGB()), 0); + v.setProperty (DrawableImageHelpers::overlay, String::toHexString ((int) overlayColour.getARGB()), 0); + + if (! getTransform().isIdentity()) + { + v.setProperty (DrawableImageHelpers::topLeft, DrawableImageHelpers::pointToString (controlPoints[0]), 0); + v.setProperty (DrawableImageHelpers::topRight, DrawableImageHelpers::pointToString (controlPoints[1]), 0); + v.setProperty (DrawableImageHelpers::bottomLeft, DrawableImageHelpers::pointToString (controlPoints[2]), 0); + } if (image != 0) { - MemoryOutputStream imageData; - PNGImageFormat pngFormat; - if (pngFormat.writeImageToStream (*image, imageData)) - { - String base64 (MemoryBlock (imageData.getData(), imageData.getDataSize()).toBase64Encoding()); + jassert (imageProvider != 0); // if you're using images, you need to provide something that can load and save them! - for (int i = (base64.length() & ~127); i >= 0; i -= 128) - base64 = base64.substring (0, i) + "\n" + base64.substring (i); - - v.setProperty ("data", base64, 0); - } + if (imageProvider != 0) + v.setProperty (DrawableImageHelpers::image, imageProvider->getIdentifierForImage (image), 0); } return v; } -DrawableImage* DrawableImage::createFromValueTree (const ValueTree& tree) -{ - if (! tree.hasType ("Image")) - return 0; - - DrawableImage* di = new DrawableImage(); - - di->setName (tree ["id"]); - di->opacity = tree.hasProperty ("opacity") ? (float) tree ["opacity"] : 1.0f; - di->overlayColour = Colour (tree ["overlay"].toString().getHexValue32()); - - MemoryBlock imageData; - if (imageData.fromBase64Encoding (tree ["data"])) - { - Image* const im = ImageFileFormat::loadFrom (imageData.getData(), (int) imageData.getSize()); - if (im == 0) - return false; - - di->setImage (im, true); - } - - return di; -} - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/drawables/juce_DrawableImage.h b/src/gui/graphics/drawables/juce_DrawableImage.h index a5ecbdda70..3a1f11b996 100644 --- a/src/gui/graphics/drawables/juce_DrawableImage.h +++ b/src/gui/graphics/drawables/juce_DrawableImage.h @@ -55,9 +55,6 @@ public: /** Sets the image that this drawable will render. - An internal copy of this will not be made, so the caller mustn't delete - the image while it's still being used by this object. - A good way to use this is with the ImageCache - if you create an image with ImageCache and pass it in here with releaseWhenNotNeeded = true, then it'll be released neatly with its reference count being decreased. @@ -68,8 +65,7 @@ public: needs it - unless the image was created by the ImageCache, in which case it will be released with ImageCache::release(). */ - void setImage (Image* imageToUse, - bool releaseWhenNotNeeded); + void setImage (Image* imageToUse, bool releaseWhenNotNeeded); /** Returns the current image. */ Image* getImage() const throw() { return image; } @@ -97,6 +93,37 @@ public: /** Returns the overlay colour. */ const Colour& getOverlayColour() const throw() { return overlayColour; } + /** Sets the transform to be applied to this image, by defining the positions + where three anchor points should end up in the target rendering space. + + @param imageTopLeftPosition the position that the image's top-left corner should be mapped to + in the target coordinate space. + @param imageTopRightPosition the position that the image's top-right corner should be mapped to + in the target coordinate space. + @param imageBottomLeftPosition the position that the image's bottom-left corner should be mapped to + in the target coordinate space. + */ + void setTransform (const Point& imageTopLeftPosition, + const Point& imageTopRightPosition, + const Point& imageBottomLeftPosition); + + /** Returns the position to which the image's top-left corner should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForTopLeft() const throw() { return controlPoints[0]; } + + /** Returns the position to which the image's top-right corner should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForTopRight() const throw() { return controlPoints[1]; } + + /** Returns the position to which the image's bottom-left corner should be remapped in the target + coordinate space when rendering this object. + @see setTransform + */ + const Point& getTargetPositionForBottomLeft() const throw() { return controlPoints[2]; } //============================================================================== /** @internal */ @@ -108,9 +135,13 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawableImage* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } //============================================================================== juce_UseDebuggingNewOperator @@ -120,6 +151,9 @@ private: bool canDeleteImage; float opacity; Colour overlayColour; + Point controlPoints[3]; + + const AffineTransform getTransform() const; DrawableImage (const DrawableImage&); DrawableImage& operator= (const DrawableImage&); diff --git a/src/gui/graphics/drawables/juce_DrawablePath.cpp b/src/gui/graphics/drawables/juce_DrawablePath.cpp index c4c81ca483..4a689e8c87 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.cpp +++ b/src/gui/graphics/drawables/juce_DrawablePath.cpp @@ -129,119 +129,159 @@ Drawable* DrawablePath::createCopy() const } //============================================================================== -static const FillType readFillTypeFromTree (const ValueTree& v) +const Identifier DrawablePath::valueTreeType ("Path"); + +namespace DrawablePathHelpers { - const String type (v["type"].toString()); + static const Identifier type ("type"); + static const Identifier solid ("solid"); + static const Identifier colour ("colour"); + static const Identifier gradient ("gradient"); + static const Identifier x1 ("x1"); + static const Identifier x2 ("x2"); + static const Identifier y1 ("y1"); + static const Identifier y2 ("y2"); + static const Identifier radial ("radial"); + static const Identifier colours ("colours"); + static const Identifier fill ("fill"); + static const Identifier stroke ("stroke"); + static const Identifier jointStyle ("jointStyle"); + static const Identifier capStyle ("capStyle"); + static const Identifier strokeWidth ("strokeWidth"); + static const Identifier path ("path"); - if (type.equalsIgnoreCase ("solid")) + static bool updateFillType (const ValueTree& v, FillType& fillType) { - const String colour (v ["colour"].toString()); - return Colour (colour.isEmpty() ? (uint32) 0xff000000 - : (uint32) colour.getHexValue32()); - } - else if (type.equalsIgnoreCase ("gradient")) - { - ColourGradient g; - g.point1.setXY (v["x1"], v["y1"]); - g.point2.setXY (v["x2"], v["y2"]); - g.isRadial = v["radial"]; + const String type (v[type].toString()); - StringArray colours; - colours.addTokens (v["colours"].toString(), false); + if (type.equalsIgnoreCase (solid)) + { + const String colourString (v [colour].toString()); + const Colour newColour (colourString.isEmpty() ? (uint32) 0xff000000 + : (uint32) colourString.getHexValue32()); - for (int i = 0; i < colours.size() / 2; ++i) - g.addColour (colours[i * 2].getDoubleValue(), - Colour ((uint32) colours[i * 2 + 1].getHexValue32())); + if (fillType.isColour() && fillType.colour == newColour) + return false; - return g; + fillType.setColour (newColour); + return true; + } + else if (type.equalsIgnoreCase (gradient)) + { + ColourGradient g; + g.point1.setXY (v[x1], v[y1]); + g.point2.setXY (v[x2], v[y2]); + g.isRadial = v[radial]; + + StringArray colourSteps; + colourSteps.addTokens (v[colours].toString(), false); + + for (int i = 0; i < colourSteps.size() / 2; ++i) + g.addColour (colourSteps[i * 2].getDoubleValue(), + Colour ((uint32) colourSteps[i * 2 + 1].getHexValue32())); + + if (fillType.isGradient() && *fillType.gradient == g) + return false; + + fillType.setGradient (g); + return true; + } + + jassertfalse; + return false; } - jassertfalse; - return FillType(); + static ValueTree createFillType (const Identifier& tagName, const FillType& fillType) + { + ValueTree v (tagName); + + if (fillType.isColour()) + { + v.setProperty (type, "solid", 0); + v.setProperty (colour, String::toHexString ((int) fillType.colour.getARGB()), 0); + } + else if (fillType.isGradient()) + { + v.setProperty (type, "gradient", 0); + v.setProperty (x1, fillType.gradient->point1.getX(), 0); + v.setProperty (y1, fillType.gradient->point1.getY(), 0); + v.setProperty (x2, fillType.gradient->point2.getX(), 0); + v.setProperty (y2, fillType.gradient->point2.getY(), 0); + v.setProperty (radial, fillType.gradient->isRadial, 0); + + String s; + for (int i = 0; i < fillType.gradient->getNumColours(); ++i) + s << " " << fillType.gradient->getColourPosition (i) + << " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); + + v.setProperty (colours, s.trimStart(), 0); + } + else + { + jassertfalse; //xxx + } + + return v; + } } -static ValueTree createTreeForFillType (const String& tagName, const FillType& fillType) +const Rectangle DrawablePath::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - ValueTree v (tagName); + jassert (tree.hasType (valueTreeType)); - if (fillType.isColour()) - { - v.setProperty ("type", "solid", 0); - v.setProperty ("colour", String::toHexString ((int) fillType.colour.getARGB()), 0); - } - else if (fillType.isGradient()) - { - v.setProperty ("type", "gradient", 0); - v.setProperty ("x1", fillType.gradient->point1.getX(), 0); - v.setProperty ("y1", fillType.gradient->point1.getY(), 0); - v.setProperty ("x2", fillType.gradient->point2.getX(), 0); - v.setProperty ("y2", fillType.gradient->point2.getY(), 0); - v.setProperty ("radial", fillType.gradient->isRadial, 0); + Rectangle damageRect; + setName (tree [idProperty]); - String s; - for (int i = 0; i < fillType.gradient->getNumColours(); ++i) - s << " " << fillType.gradient->getColourPosition (i) - << " " << String::toHexString ((int) fillType.gradient->getColour(i).getARGB()); + bool needsRedraw = DrawablePathHelpers::updateFillType (tree.getChildWithName (DrawablePathHelpers::fill), mainFill); + needsRedraw = DrawablePathHelpers::updateFillType (tree.getChildWithName (DrawablePathHelpers::stroke), strokeFill) || needsRedraw; - v.setProperty ("colours", s.trimStart(), 0); - } - else + const String jointStyle (tree [DrawablePathHelpers::jointStyle].toString()); + const String endStyle (tree [DrawablePathHelpers::capStyle].toString()); + + PathStrokeType newStroke (tree [DrawablePathHelpers::strokeWidth], + jointStyle == "curved" ? PathStrokeType::curved + : (jointStyle == "bevel" ? PathStrokeType::beveled + : PathStrokeType::mitered), + endStyle == "square" ? PathStrokeType::square + : (endStyle == "round" ? PathStrokeType::rounded + : PathStrokeType::butt)); + + Path newPath; + newPath.restoreFromString (tree [DrawablePathHelpers::path]); + + if (strokeType != newStroke || path != newPath) { - jassertfalse; //xxx + damageRect = getBounds(); + path.swapWithPath (newPath); + strokeType = newStroke; + needsRedraw = true; } - return v; + if (needsRedraw) + damageRect = damageRect.getUnion (getBounds()); + + return damageRect; } -ValueTree DrawablePath::createValueTree() const +const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const { - ValueTree v ("Path"); + ValueTree v (valueTreeType); - v.addChild (createTreeForFillType ("fill", mainFill), -1, 0); - v.addChild (createTreeForFillType ("stroke", strokeFill), -1, 0); + v.addChild (DrawablePathHelpers::createFillType (DrawablePathHelpers::fill, mainFill), -1, 0); + v.addChild (DrawablePathHelpers::createFillType (DrawablePathHelpers::stroke, strokeFill), -1, 0); if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); + v.setProperty (idProperty, getName(), 0); - v.setProperty ("strokeWidth", (double) strokeType.getStrokeThickness(), 0); - v.setProperty ("jointStyle", strokeType.getJointStyle() == PathStrokeType::mitered - ? "miter" : (strokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), 0); - v.setProperty ("capStyle", strokeType.getEndStyle() == PathStrokeType::butt - ? "butt" : (strokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), 0); - v.setProperty ("path", path.toString(), 0); + v.setProperty (DrawablePathHelpers::strokeWidth, (double) strokeType.getStrokeThickness(), 0); + v.setProperty (DrawablePathHelpers::jointStyle, strokeType.getJointStyle() == PathStrokeType::mitered + ? "miter" : (strokeType.getJointStyle() == PathStrokeType::curved ? "curved" : "bevel"), 0); + v.setProperty (DrawablePathHelpers::capStyle, strokeType.getEndStyle() == PathStrokeType::butt + ? "butt" : (strokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), 0); + v.setProperty (DrawablePathHelpers::path, path.toString(), 0); return v; } -DrawablePath* DrawablePath::createFromValueTree (const ValueTree& tree) -{ - if (! tree.hasType ("Path")) - return 0; - - DrawablePath* p = new DrawablePath(); - - p->setName (tree ["id"]); - p->mainFill = readFillTypeFromTree (tree.getChildWithName ("fill")); - p->strokeFill = readFillTypeFromTree (tree.getChildWithName ("stroke")); - - const String jointStyle (tree ["jointStyle"].toString()); - const String endStyle (tree ["capStyle"].toString()); - - p->strokeType - = PathStrokeType (tree ["strokeWidth"], - jointStyle.equalsIgnoreCase ("curved") ? PathStrokeType::curved - : (jointStyle.equalsIgnoreCase ("bevel") ? PathStrokeType::beveled - : PathStrokeType::mitered), - endStyle.equalsIgnoreCase ("square") ? PathStrokeType::square - : (endStyle.equalsIgnoreCase ("round") ? PathStrokeType::rounded - : PathStrokeType::butt)); - - p->path.clear(); - p->path.restoreFromString (tree ["path"]); - p->updateOutline(); - - return p; -} - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/drawables/juce_DrawablePath.h b/src/gui/graphics/drawables/juce_DrawablePath.h index ae9aa31a23..4aaef11979 100644 --- a/src/gui/graphics/drawables/juce_DrawablePath.h +++ b/src/gui/graphics/drawables/juce_DrawablePath.h @@ -107,9 +107,13 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawablePath* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } //============================================================================== juce_UseDebuggingNewOperator diff --git a/src/gui/graphics/drawables/juce_DrawableText.cpp b/src/gui/graphics/drawables/juce_DrawableText.cpp index 03bd4bb7f7..1c9a63371e 100644 --- a/src/gui/graphics/drawables/juce_DrawableText.cpp +++ b/src/gui/graphics/drawables/juce_DrawableText.cpp @@ -85,30 +85,28 @@ Drawable* DrawableText::createCopy() const } //============================================================================== -ValueTree DrawableText::createValueTree() const +const Identifier DrawableText::valueTreeType ("Text"); + +const Rectangle DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) { - ValueTree v ("Text"); + jassert (tree.hasType (valueTreeType)); + setName (tree [idProperty]); + + jassertfalse; // xxx not finished! + + return Rectangle(); +} + +const ValueTree DrawableText::createValueTree (ImageProvider* imageProvider) const +{ + ValueTree v (valueTreeType); if (getName().isNotEmpty()) - v.setProperty ("id", getName(), 0); + v.setProperty (idProperty, getName(), 0); jassertfalse; // xxx not finished! return v; } -DrawableText* DrawableText::createFromValueTree (const ValueTree& tree) -{ - if (! tree.hasType ("Text")) - return 0; - - DrawableText* dt = new DrawableText(); - - dt->setName (tree ["id"]); - - jassertfalse; // xxx not finished! - - return dt; -} - END_JUCE_NAMESPACE diff --git a/src/gui/graphics/drawables/juce_DrawableText.h b/src/gui/graphics/drawables/juce_DrawableText.h index 5f1c230217..324a081593 100644 --- a/src/gui/graphics/drawables/juce_DrawableText.h +++ b/src/gui/graphics/drawables/juce_DrawableText.h @@ -78,9 +78,13 @@ public: /** @internal */ Drawable* createCopy() const; /** @internal */ - ValueTree createValueTree() const; + const Rectangle refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); /** @internal */ - static DrawableText* createFromValueTree (const ValueTree& tree); + const ValueTree createValueTree (ImageProvider* imageProvider) const; + /** @internal */ + static const Identifier valueTreeType; + /** @internal */ + const Identifier getValueTreeType() const { return valueTreeType; } //============================================================================== juce_UseDebuggingNewOperator diff --git a/src/gui/graphics/fonts/juce_GlyphArrangement.cpp b/src/gui/graphics/fonts/juce_GlyphArrangement.cpp index d98adcfb04..a997b7c194 100644 --- a/src/gui/graphics/fonts/juce_GlyphArrangement.cpp +++ b/src/gui/graphics/fonts/juce_GlyphArrangement.cpp @@ -615,24 +615,13 @@ const Rectangle GlyphArrangement::getBoundingBox (int startIndex, int num num = glyphs.size() - startIndex; Rectangle result; - bool isFirst = true; while (--num >= 0) { const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); if (includeWhitespace || ! pg->isWhitespace()) - { - if (isFirst) - { - isFirst = false; - result = pg->getBounds(); - } - else - { - result = result.getUnion (pg->getBounds()); - } - } + result = result.getUnion (pg->getBounds()); } return result; diff --git a/src/gui/graphics/geometry/juce_AffineTransform.cpp b/src/gui/graphics/geometry/juce_AffineTransform.cpp index 6a3d73e05f..f835f0c920 100644 --- a/src/gui/graphics/geometry/juce_AffineTransform.cpp +++ b/src/gui/graphics/geometry/juce_AffineTransform.cpp @@ -233,6 +233,14 @@ bool AffineTransform::isSingularity() const throw() return (mat00 * mat11 - mat10 * mat01) == 0.0; } +const AffineTransform AffineTransform::fromTargetPoints (const float x00, const float y00, + const float x10, const float y10, + const float x01, const float y01) throw() +{ + return AffineTransform (x10 - x00, x01 - x00, x00, + y10 - y00, y01 - y00, y00); +} + bool AffineTransform::isOnlyTranslation() const throw() { return (mat01 == 0) diff --git a/src/gui/graphics/geometry/juce_AffineTransform.h b/src/gui/graphics/geometry/juce_AffineTransform.h index 4e6ea96699..e1379b57a6 100644 --- a/src/gui/graphics/geometry/juce_AffineTransform.h +++ b/src/gui/graphics/geometry/juce_AffineTransform.h @@ -146,6 +146,16 @@ public: */ const AffineTransform inverted() const throw(); + /** Returns the transform that will map three known points onto three coordinates + that are supplied. + + This returns the transform that will transform (0, 0) into (x00, y00), + (1, 0) to (x10, y10), and (0, 1) to (x01, y01). + */ + static const AffineTransform fromTargetPoints (float x00, float y00, + float x10, float y10, + float x01, float y01) throw(); + //============================================================================== /** Returns the result of concatenating another transformation after this one. */ const AffineTransform followedBy (const AffineTransform& other) const throw(); diff --git a/src/gui/graphics/geometry/juce_Path.cpp b/src/gui/graphics/geometry/juce_Path.cpp index c3ff7ff8a6..c76c538d91 100644 --- a/src/gui/graphics/geometry/juce_Path.cpp +++ b/src/gui/graphics/geometry/juce_Path.cpp @@ -141,6 +141,23 @@ Path& Path::operator= (const Path& other) return *this; } +bool Path::operator== (const Path& other) const throw() +{ + return ! operator!= (other); +} + +bool Path::operator!= (const Path& other) const throw() +{ + if (numElements != other.numElements || useNonZeroWinding != other.useNonZeroWinding) + return true; + + for (int i = 0; i < numElements; ++i) + if (data.elements[i] != other.data.elements[i]) + return true; + + return false; +} + void Path::clear() throw() { numElements = 0; @@ -150,7 +167,7 @@ void Path::clear() throw() pathXMax = 0; } -void Path::swapWithPath (Path& other) +void Path::swapWithPath (Path& other) throw() { data.swapWith (other.data); swapVariables (numElements, other.numElements); @@ -204,7 +221,6 @@ const Rectangle Path::getBounds() const throw() pathYMax - pathYMin); } - const Rectangle Path::getBoundsTransformed (const AffineTransform& transform) const throw() { return getBounds().transformed (transform); diff --git a/src/gui/graphics/geometry/juce_Path.h b/src/gui/graphics/geometry/juce_Path.h index c158dd7482..24052e9ceb 100644 --- a/src/gui/graphics/geometry/juce_Path.h +++ b/src/gui/graphics/geometry/juce_Path.h @@ -84,6 +84,9 @@ public: /** Copies this path from another one. */ Path& operator= (const Path& other); + bool operator== (const Path& other) const throw(); + bool operator!= (const Path& other) const throw(); + //============================================================================== /** Returns true if the path doesn't contain any lines or curves. */ bool isEmpty() const throw(); @@ -472,7 +475,7 @@ public: The internal data of the two paths is swapped over, so this is much faster than copying it to a temp variable and back. */ - void swapWithPath (Path& other); + void swapWithPath (Path& other) throw(); //============================================================================== /** Applies a 2D transform to all the vertices in the path. diff --git a/src/gui/graphics/geometry/juce_Point.h b/src/gui/graphics/geometry/juce_Point.h index 9975044dda..459049a303 100644 --- a/src/gui/graphics/geometry/juce_Point.h +++ b/src/gui/graphics/geometry/juce_Point.h @@ -138,6 +138,9 @@ public: /** Returns the position of this point, if it is transformed by a given AffineTransform. */ const Point transformedBy (const AffineTransform& transform) const throw() { ValueType x2 (x), y2 (y); transform.transformPoint (x2, y2); return Point (x2, y2); } + /** Casts this point to a Point object. */ + const Point toFloat() const throw() { return Point (static_cast (x), static_cast (y)); } + /** Returns the point as a string in the form "x, y". */ const String toString() const { return String (x) + ", " + String (y); } diff --git a/src/gui/graphics/geometry/juce_Rectangle.h b/src/gui/graphics/geometry/juce_Rectangle.h index bf53b38d7f..26be95302e 100644 --- a/src/gui/graphics/geometry/juce_Rectangle.h +++ b/src/gui/graphics/geometry/juce_Rectangle.h @@ -395,11 +395,16 @@ public: return false; } - /** Returns the smallest rectangle that contains both this one and the one - passed-in. + /** Returns the smallest rectangle that contains both this one and the one passed-in. + + If either this or the other rectangle are empty, they will not be counted as + part of the resulting region. */ const Rectangle getUnion (const Rectangle& other) const throw() { + if (other.isEmpty()) return *this; + if (isEmpty()) return other; + const ValueType newX = jmin (x, other.x); const ValueType newY = jmin (y, other.y); diff --git a/src/native/linux/juce_linux_Messaging.cpp b/src/native/linux/juce_linux_Messaging.cpp index 606209b764..ed75dfc232 100644 --- a/src/native/linux/juce_linux_Messaging.cpp +++ b/src/native/linux/juce_linux_Messaging.cpp @@ -127,18 +127,6 @@ private: } }; -//============================================================================== -struct MessageThreadFuncCall -{ - enum { uniqueID = 0x73774623 }; - - MessageCallbackFunction* func; - void* parameter; - void* result; - CriticalSection lock; - WaitableEvent event; -}; - //============================================================================== static InternalMessageQueue* juce_internalMessageQueue = 0; @@ -327,6 +315,17 @@ void MessageManager::broadcastMessage (const String& value) throw() /* TODO */ } +struct MessageThreadFuncCall +{ + enum { uniqueID = 0x73774623 }; + + MessageCallbackFunction* func; + void* parameter; + void* result; + CriticalSection lock; + WaitableEvent event; +}; + void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) { diff --git a/src/native/mac/juce_iphone_MessageManager.mm b/src/native/mac/juce_iphone_MessageManager.mm index 93d3355440..b2e3d54df1 100644 --- a/src/native/mac/juce_iphone_MessageManager.mm +++ b/src/native/mac/juce_iphone_MessageManager.mm @@ -36,81 +36,24 @@ struct CallbackMessagePayload }; END_JUCE_NAMESPACE -using namespace JUCE_NAMESPACE; -@interface JuceAppDelegate : NSObject +//============================================================================== +@interface JuceCustomMessageHandler : NSObject { } -- (JuceAppDelegate*) init; -- (void) dealloc; -- (BOOL) application: (UIApplication*) application handleOpenURL: (NSURL*) url; -- (void) applicationDidBecomeActive: (NSNotification*) aNotification; -- (void) applicationDidResignActive: (NSNotification*) aNotification; -- (void) applicationWillUnhide: (NSNotification*) aNotification; -- (void) customEvent: (id) data; - (void) performCallback: (id) info; + @end -@implementation JuceAppDelegate - -- (JuceAppDelegate*) init -{ - [super init]; - - [[UIApplication sharedApplication] setDelegate: self]; - - return self; -} - -- (void) dealloc -{ - [[UIApplication sharedApplication] setDelegate: nil]; - [super dealloc]; -} - -- (BOOL) application: (UIApplication*) application handleOpenURL: (NSURL*) url -{ - if (JUCEApplication::getInstance() != 0) - { - JUCEApplication::getInstance()->anotherInstanceStarted (nsStringToJuce ([url absoluteString])); - return YES; - } - - return NO; -} - -- (void) applicationDidBecomeActive: (NSNotification*) aNotification -{ - juce_HandleProcessFocusChange(); -} - -- (void) applicationDidResignActive: (NSNotification*) aNotification -{ - juce_HandleProcessFocusChange(); -} - -- (void) applicationWillUnhide: (NSNotification*) aNotification -{ - juce_HandleProcessFocusChange(); -} - -- (void) customEvent: (id) n -{ - NSData* data = (NSData*) n; - void* message = 0; - [data getBytes: &message length: sizeof (message)]; - [data release]; - - if (message != 0) - MessageManager::getInstance()->deliverMessage (message); -} +//============================================================================== +@implementation JuceCustomMessageHandler - (void) performCallback: (id) info { if ([info isKindOfClass: [NSData class]]) { - CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes]; + JUCE_NAMESPACE::CallbackMessagePayload* pl = (JUCE_NAMESPACE::CallbackMessagePayload*) [((NSData*) info) bytes]; if (pl != 0) { @@ -128,8 +71,7 @@ using namespace JUCE_NAMESPACE; BEGIN_JUCE_NAMESPACE -static JuceAppDelegate* juceAppDelegate = 0; - +//============================================================================== void MessageManager::runDispatchLoop() { jassert (isThisTheMessageThread()); // must only be called by the message thread @@ -167,6 +109,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) static CFRunLoopRef runLoop = 0; static CFRunLoopSourceRef runLoopSource = 0; static Array * pendingMessages = 0; +static JuceCustomMessageHandler* juceCustomMessageHandler = 0; static void runLoopSourceCallback (void*) { @@ -202,8 +145,8 @@ void MessageManager::doPlatformSpecificInitialisation() runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); - if (juceAppDelegate == 0) - juceAppDelegate = [[JuceAppDelegate alloc] init]; + if (juceCustomMessageHandler == 0) + juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; } void MessageManager::doPlatformSpecificShutdown() @@ -220,11 +163,11 @@ void MessageManager::doPlatformSpecificShutdown() deleteAndZero (pendingMessages); } - if (juceAppDelegate != 0) + if (juceCustomMessageHandler != 0) { - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceAppDelegate]; - [juceAppDelegate release]; - juceAppDelegate = 0; + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: juceCustomMessageHandler]; + [juceCustomMessageHandler release]; + juceCustomMessageHandler = 0; } } @@ -266,11 +209,11 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* call cmp.result = 0; cmp.hasBeenExecuted = false; - [juceAppDelegate performSelectorOnMainThread: @selector (performCallback:) - withObject: [NSData dataWithBytesNoCopy: &cmp - length: sizeof (cmp) - freeWhenDone: NO] - waitUntilDone: YES]; + [juceCustomMessageHandler performSelectorOnMainThread: @selector (performCallback:) + withObject: [NSData dataWithBytesNoCopy: &cmp + length: sizeof (cmp) + freeWhenDone: NO] + waitUntilDone: YES]; return cmp.result; }