| @@ -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"); | |||
| } | |||
| @@ -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&); | |||
| @@ -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: | |||
| @@ -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; | |||
| @@ -96,8 +96,8 @@ public: | |||
| props.add (new ChoicePropertyComponent (item.getValue (Ids::textBoxPos), "Text Box", StringArray (textBoxPositions), Array<var> (positionValues, numElementsInArray (positionValues)))); | |||
| props.add (new BooleanPropertyComponent (item.getValue (Ids::editable), "Editable", "Value can be edited")); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (item.getValue ("textBoxWidth"))), "Text Box Width", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (item.getValue ("textBoxHeight"))), "Text Box Height", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (item.getValue (Ids::textBoxWidth))), "Text Box Width", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<int> (item.getValue (Ids::textBoxHeight))), "Text Box Height", 8, false)); | |||
| props.add (new TextPropertyComponent (Value (new NumericValueSource<double> (item.getValue (Ids::skew))), "Skew Factor", 16, false)); | |||
| addEditableColourProperties (item, props); | |||
| @@ -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 (objectName == Coordinate::Strings::parent) | |||
| { | |||
| if (edge == Coordinate::Strings::right) return Coordinate ((double) getCanvasWidth().getValue(), true); | |||
| if (edge == Coordinate::Strings::bottom) return Coordinate ((double) getCanvasHeight().getValue(), false); | |||
| } | |||
| if (name.containsChar ('.')) | |||
| if (objectName.isNotEmpty() && edge.isNotEmpty()) | |||
| { | |||
| const String compName (name.upToFirstOccurrenceOf (".", false, false).trim()); | |||
| const String edge (name.fromFirstOccurrenceOf (".", false, false).trim()); | |||
| const ValueTree comp (getComponentWithMemberName (objectName)); | |||
| if (compName.isNotEmpty() && edge.isNotEmpty()) | |||
| 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)); | |||
| @@ -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 <PropertyComponent*>& 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); } | |||
| @@ -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 (objectName == "parent") | |||
| { | |||
| if (edge == "right") return Coordinate ((double) getCanvasWidth().getValue(), true); | |||
| if (edge == "bottom") return Coordinate ((double) getCanvasHeight().getValue(), false); | |||
| } | |||
| if (name.containsChar ('.')) | |||
| if (objectName.isNotEmpty() && edge.isNotEmpty()) | |||
| { | |||
| const String compName (name.upToFirstOccurrenceOf (".", false, false).trim()); | |||
| const String edge (name.fromFirstOccurrenceOf (".", false, false).trim()); | |||
| /* const ValueTree comp (getComponentWithMemberName (compName)); | |||
| if (compName.isNotEmpty() && edge.isNotEmpty()) | |||
| if (comp.isValid()) | |||
| { | |||
| /* const ValueTree comp (getComponentWithMemberName (compName)); | |||
| const RectangleCoordinates coords (getCoordsFor (comp)); | |||
| 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; | |||
| }*/ | |||
| } | |||
| if (edge == "left") 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 (getMarkerList (isHorizontal).getMarkerNamed (name)); | |||
| if (marker.isValid()) | |||
| return getMarkerList (isHorizontal).getCoordinate (marker); | |||
| { | |||
| const ValueTree marker (getMarkerListY().getMarkerNamed (objectName)); | |||
| if (marker.isValid()) | |||
| return getMarkerListY().getCoordinate (marker); | |||
| } | |||
| return Coordinate (isHorizontal); | |||
| 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 <PropertyComponent*>& props, const String& itemId) | |||
| @@ -334,10 +341,10 @@ bool DrawableDocument::MarkerList::createProperties (Array <PropertyComponent*>& | |||
| 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; | |||
| } | |||
| @@ -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 <PropertyComponent*>& 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); | |||
| }; | |||
| @@ -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); | |||
| } | |||
| @@ -221,24 +221,24 @@ public: | |||
| void createPropertyEditors (Array <PropertyComponent*>& 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); | |||
| @@ -128,10 +128,10 @@ public: | |||
| const int libTypeValues[] = { 1, 2, 0 }; | |||
| props.add (new ChoicePropertyComponent (getLibraryType(), "Library Type", StringArray (libTypes), Array<var> (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; | |||
| @@ -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 <const juce_wchar*> (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); | |||
| @@ -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: <foo/bar.h> 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<RelativePath> juceWrapperFiles; | |||
| @@ -91,9 +91,9 @@ public: | |||
| return editor.getSelection(); | |||
| } | |||
| void getSelectedItemProperties (Array<PropertyComponent*>& newComps) | |||
| void getSelectedItemProperties (Array<PropertyComponent*>& 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 <PropertyComponent*>& props) | |||
| { | |||
| getDocument().createItemProperties (props, getSelectedIds()); | |||
| } | |||
| void ComponentEditor::deleteSelection() | |||
| { | |||
| const StringArray ids (getSelectedIds()); | |||
| @@ -56,7 +56,6 @@ public: | |||
| ComponentDocument& getDocument() const { return *componentDocument; } | |||
| const StringArray getSelectedIds() const; | |||
| void getSelectedItemProperties (Array <PropertyComponent*>& props); | |||
| void deleteSelection(); | |||
| void deselectNonComponents(); | |||
| void selectionToFront(); | |||
| @@ -90,9 +90,9 @@ public: | |||
| return String::empty; | |||
| } | |||
| void showPopupMenu (const Point<int>& position) | |||
| void showPopupMenu (bool isClickOnSelectedObject) | |||
| { | |||
| if (findObjectIdAt (position).isNotEmpty()) | |||
| if (isClickOnSelectedObject) | |||
| { | |||
| PopupMenu m; | |||
| m.addCommandItem (commandManager, CommandIDs::toFront); | |||
| @@ -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<int>& 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; | |||
| @@ -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.. | |||
| @@ -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<String>& 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 <CommandID>& 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); | |||
| } | |||
| @@ -42,9 +42,21 @@ public: | |||
| Project* project, DrawableDocument* drawableDocument); | |||
| ~DrawableEditor(); | |||
| //============================================================================== | |||
| void getAllCommands (Array <CommandID>& 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; } | |||
| @@ -74,11 +74,11 @@ public: | |||
| return String::empty; | |||
| } | |||
| void showPopupMenu (const Point<int>& position) | |||
| void showPopupMenu (bool isClickOnSelectedObject) | |||
| { | |||
| PopupMenu m; | |||
| if (findObjectIdAt (position).isNotEmpty()) | |||
| if (isClickOnSelectedObject) | |||
| { | |||
| m.addCommandItem (commandManager, CommandIDs::toFront); | |||
| m.addCommandItem (commandManager, CommandIDs::toBack); | |||
| @@ -41,17 +41,22 @@ public: | |||
| } | |||
| //============================================================================== | |||
| enum DemoToolbarItemIds | |||
| enum ToolbarItemIds | |||
| { | |||
| createComponent = 1, | |||
| showInfo = 2, | |||
| showComponentTree = 3, | |||
| createShape = 1, | |||
| showInfo, | |||
| showTree, | |||
| showOrHideMarkers, | |||
| toggleSnapping | |||
| }; | |||
| void getAllToolbarItemIds (Array <int>& 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 <int>& 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; | |||
| @@ -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 <Drawable> d (Drawable::createFromValueTree (drawableRoot)); | |||
| if (d != 0) | |||
| { | |||
| if (dynamic_cast <DrawablePath*> ((Drawable*) d) != 0) | |||
| typeName = "Path"; | |||
| else if (dynamic_cast <DrawableImage*> ((Drawable*) d) != 0) | |||
| typeName = "Image"; | |||
| else if (dynamic_cast <DrawableComposite*> ((Drawable*) d) != 0) | |||
| typeName = "Group"; | |||
| else if (dynamic_cast <DrawableText*> ((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 <XmlElement> 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 <XmlElement> 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); | |||
| if (oldOpenness != 0) | |||
| restoreOpennessState (*oldOpenness); | |||
| editor.getSelection().changed(); | |||
| editor.getSelection().changed(); | |||
| } | |||
| } | |||
| const String getDisplayName() const | |||
| @@ -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<int>& bounds) | |||
| { | |||
| RectangleCoordinates coords (canvas->getObjectCoords (state)); | |||
| Coordinate coord (false); | |||
| Coordinate coord; | |||
| Rectangle<int> r; | |||
| switch (type) | |||
| @@ -174,6 +199,7 @@ private: | |||
| ResizableBorderComponent::Zone dragZone; | |||
| const int borderThickness; | |||
| OwnedArray <SizeGuideComponent> sizeGuides; | |||
| bool isDragging; | |||
| const Rectangle<int> 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 | |||
| { | |||
| @@ -72,7 +72,7 @@ public: | |||
| virtual MarkerListBase& getMarkerList (bool isX) = 0; | |||
| virtual const SelectedItems::ItemType findObjectIdAt (const Point<int>& position) = 0; | |||
| virtual void showPopupMenu (const Point<int>& 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; | |||
| @@ -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); | |||
| } | |||
| bool Coordinate::isOrigin (const String& name) | |||
| const Coordinate Coordinate::getAnchorCoordinate1() const | |||
| { | |||
| return Coordinate (0.0, anchor1); | |||
| } | |||
| const Coordinate Coordinate::getAnchorCoordinate2() const | |||
| { | |||
| return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName; | |||
| return Coordinate (0.0, anchor2); | |||
| } | |||
| const String Coordinate::getOriginMarkerName() const | |||
| bool Coordinate::isOrigin (const String& name) | |||
| { | |||
| return horizontal ? parentLeftMarkerName : parentTopMarkerName; | |||
| return name.isEmpty() | |||
| || name == Strings::originX | |||
| || name == Strings::originY; | |||
| } | |||
| const String Coordinate::getExtentMarkerName() const | |||
| const String Coordinate::getOriginAnchorName (const bool isHorizontal) const throw() | |||
| { | |||
| return horizontal ? parentRightMarkerName : parentBottomMarkerName; | |||
| return isHorizontal ? Strings::originX : Strings::originY; | |||
| } | |||
| const String Coordinate::checkName (const String& name) const | |||
| const String Coordinate::getExtentAnchorName (const bool isHorizontal) const throw() | |||
| { | |||
| return name.isEmpty() ? getOriginMarkerName() : name; | |||
| return isHorizontal ? Strings::extentX : Strings::extentY; | |||
| } | |||
| double Coordinate::getPosition (const String& name, const MarkerResolver& markerResolver, int recursionCounter) const | |||
| //============================================================================== | |||
| Coordinate::RecursiveCoordinateException::RecursiveCoordinateException() | |||
| : std::runtime_error ("Coordinate::RecursiveCoordinateException") | |||
| { | |||
| if (isOrigin (name)) | |||
| return 0.0; | |||
| } | |||
| return markerResolver.findMarker (name, horizontal) | |||
| .resolve (markerResolver, recursionCounter + 1); | |||
| const Coordinate Coordinate::lookUpName (const String& name, const NamedCoordinateFinder& nameSource) const | |||
| { | |||
| return nameSource.findNamedCoordinate (getObjectName (name), getEdgeName (name)); | |||
| } | |||
| struct RecursivePositionException | |||
| double Coordinate::resolveAnchor (const String& anchorName, const NamedCoordinateFinder& nameSource, int recursionCounter) const | |||
| { | |||
| }; | |||
| if (isOrigin (anchorName)) | |||
| return 0.0; | |||
| return lookUpName (anchorName, nameSource).resolve (nameSource, recursionCounter + 1); | |||
| } | |||
| 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); | |||
| bool Coordinate::referencesIndirectly (const String& markerName, const MarkerResolver& markerResolver) const | |||
| { | |||
| if (isOrigin (anchor1) && ! isProportion) | |||
| return isOrigin (markerName); | |||
| anchor1 = getOriginAnchorName (isHorizontal); | |||
| anchor2 = isProportional() ? String::empty | |||
| : getExtentAnchorName (isHorizontal); | |||
| return referencesDirectly (markerName) | |||
| || markerResolver.findMarker (anchor1, horizontal).referencesIndirectly (markerName, markerResolver) | |||
| || (isProportion && markerResolver.findMarker (anchor2, horizontal).referencesIndirectly (markerName, markerResolver)); | |||
| moveToAbsolute (oldValue, nameSource); | |||
| } | |||
| void Coordinate::skipWhitespace (const String& s, int& i) | |||
| //============================================================================== | |||
| bool Coordinate::references (const String& coordName, const NamedCoordinateFinder& nameSource) const | |||
| { | |||
| while (CharacterFunctions::isWhitespace (s[i])) | |||
| ++i; | |||
| if (isOrigin (anchor1) && ! isProportional()) | |||
| return isOrigin (coordName); | |||
| return anchor1 == coordName | |||
| || anchor2 == coordName | |||
| || lookUpName (anchor1, nameSource).references (coordName, nameSource) | |||
| || (isProportional() && lookUpName (anchor2, nameSource).references (coordName, nameSource)); | |||
| } | |||
| const String Coordinate::readMarkerName (const String& s, int& i) | |||
| //============================================================================== | |||
| namespace CoordParserHelpers | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') | |||
| static void skipWhitespace (const String& s, int& i) | |||
| { | |||
| int start = i; | |||
| while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') | |||
| while (CharacterFunctions::isWhitespace (s[i])) | |||
| ++i; | |||
| return s.substring (start, i); | |||
| } | |||
| return String::empty; | |||
| } | |||
| static const String readAnchorName (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| double Coordinate::readNumber (const String& s, int& i) | |||
| { | |||
| skipWhitespace (s, i); | |||
| if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') | |||
| { | |||
| int start = i; | |||
| int start = i; | |||
| while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') | |||
| ++i; | |||
| if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') | |||
| ++i; | |||
| return s.substring (start, i); | |||
| } | |||
| while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') | |||
| ++i; | |||
| return String::empty; | |||
| } | |||
| if ((s[i] == 'e' || s[i] == 'E') | |||
| && (CharacterFunctions::isDigit (s[i + 1]) | |||
| || s[i + 1] == '-' | |||
| || s[i + 1] == '+')) | |||
| static double readNumber (const String& s, int& i) | |||
| { | |||
| i += 2; | |||
| skipWhitespace (s, i); | |||
| while (CharacterFunctions::isDigit (s[i])) | |||
| int start = i; | |||
| if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') | |||
| ++i; | |||
| } | |||
| const double value = s.substring (start, i).getDoubleValue(); | |||
| 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::isWhitespace (s[i]) || s[i] == ',') | |||
| ++i; | |||
| while (CharacterFunctions::isDigit (s[i])) | |||
| ++i; | |||
| } | |||
| const double value = s.substring (start, i).getDoubleValue(); | |||
| return value; | |||
| while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') | |||
| ++i; | |||
| return value; | |||
| } | |||
| static const String limitedAccuracyString (const double n) | |||
| { | |||
| return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd ("."); | |||
| } | |||
| } | |||
| 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); | |||
| isProportion = ! isProportion; | |||
| anchor1 = getOriginMarkerName(); | |||
| anchor2 = getExtentMarkerName(); | |||
| jassert (newAnchorName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_.")); | |||
| 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_.")); | |||
| void Coordinate::changeAnchor2 (const String& newMarkerName, const MarkerResolver& markerResolver) | |||
| { | |||
| const double oldValue = resolve (markerResolver); | |||
| anchor2 = newMarkerName; | |||
| moveToAbsolute (oldValue, markerResolver); | |||
| const double oldValue = resolve (nameSource); | |||
| anchor2 = newAnchorName; | |||
| moveToAbsolute (oldValue, nameSource); | |||
| } | |||
| 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<float>& 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<float> CoordinatePair::resolve (const Coordinate::NamedCoordinateFinder& nameSource) const | |||
| { | |||
| return Point<float> ((float) x.resolve (nameSource), | |||
| (float) y.resolve (nameSource)); | |||
| } | |||
| void CoordinatePair::moveToAbsolute (const Point<float>& 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<float>& 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<int> RectangleCoordinates::resolve (const Coordinate::MarkerResolver& markerResolver) const | |||
| const Rectangle<int> 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<int> (l, t, r - l, b - t); | |||
| } | |||
| void RectangleCoordinates::moveToAbsolute (const Rectangle<float>& newPos, const Coordinate::MarkerResolver& markerResolver) | |||
| void RectangleCoordinates::moveToAbsolute (const Rectangle<float>& 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) | |||
| @@ -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; | |||
| /** Returns true if this co-ordinate is expressed directly in terms of the specified marker. */ | |||
| bool referencesDirectly (const String& markerName) const; | |||
| double resolve (const NamedCoordinateFinder& nameSource) 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 coord to make it resolve to the specified position. */ | |||
| void moveToAbsolute (double newPos, const NamedCoordinateFinder& nameSource); | |||
| /** */ | |||
| bool isProportional() const throw() { return anchor2.isNotEmpty(); } | |||
| /** Changes the value of this marker to make it resolve to the specified position. */ | |||
| void moveToAbsolute (double newPos, const MarkerResolver& markerResolver); | |||
| /** */ | |||
| void toggleProportionality (const NamedCoordinateFinder& nameSource, bool isHorizontal); | |||
| const Coordinate getAnchorPoint1() const; | |||
| const Coordinate getAnchorPoint2() const; | |||
| /** */ | |||
| const double getEditableNumber() const; | |||
| const double getEditableValue() const; | |||
| void setEditableValue (const double newValue); | |||
| /** */ | |||
| void setEditableNumber (const double newValue); | |||
| bool isHorizontal() const throw() { return horizontal; } | |||
| //============================================================================== | |||
| /** */ | |||
| const String getAnchorName1() const { return anchor1; } | |||
| bool isProportional() const throw() { return isProportion; } | |||
| void toggleProportionality (const MarkerResolver& markerResolver); | |||
| /** */ | |||
| const String getAnchorName2() const { return anchor2; } | |||
| const String getAnchor1() const { return checkName (anchor1); } | |||
| void changeAnchor1 (const String& newMarkerName, const MarkerResolver& markerResolver); | |||
| /** */ | |||
| const Coordinate getAnchorCoordinate1() const; | |||
| const String getAnchor2() const { return checkName (anchor2); } | |||
| void changeAnchor2 (const String& newMarkerName, const MarkerResolver& markerResolver); | |||
| /** */ | |||
| 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<float>& absolutePoint); | |||
| CoordinatePair (const String& stringVersion); | |||
| const Point<float> resolve (const Coordinate::NamedCoordinateFinder& nameSource) const; | |||
| void moveToAbsolute (const Point<float>& 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<int> resolve (const Coordinate::MarkerResolver& markerResolver) const; | |||
| void moveToAbsolute (const Rectangle<float>& newPos, const Coordinate::MarkerResolver& markerResolver); | |||
| const Rectangle<int> resolve (const Coordinate::NamedCoordinateFinder& nameSource) const; | |||
| void moveToAbsolute (const Rectangle<float>& 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 <ComponentPosition> components; | |||
| OwnedArray <MarkerPosition> markers; | |||
| ComponentAutoLayoutManager (const ComponentAutoLayoutManager&); | |||
| ComponentAutoLayoutManager& operator= (const ComponentAutoLayoutManager&); | |||
| }; | |||
| #endif // __JUCER_COORDINATE_H_EF56ACFA__ | |||
| @@ -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) | |||
| @@ -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_) | |||
| { | |||
| @@ -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<int>& newBounds) | |||
| { | |||
| } | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| JucerToolbarButton (const JucerToolbarButton&); | |||
| JucerToolbarButton& operator= (const JucerToolbarButton&); | |||
| }; | |||
| @@ -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 <DrawableComposite*> (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<float> bounds = loadedSVG->getBounds(); | |||
| const float scaleFactor = 300.0f / jmax (bounds.getWidth(), bounds.getHeight()); | |||
| Rectangle<float> 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<float> topLeft (-bounds.getCentreX() * scaleFactor, | |||
| -bounds.getCentreY() * scaleFactor); | |||
| svgDrawable->setTransform (topLeft, | |||
| topLeft + Point<float> (scaleFactor, 0), | |||
| topLeft + Point<float> (0, scaleFactor)); | |||
| } | |||
| } | |||
| } | |||
| @@ -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<float> object. */ | |||
| const Point<float> toFloat() const throw() { return Point<float> (static_cast <float> (x), static_cast<float> (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, 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. | |||
| */ | |||
| static Drawable* createFromValueTree (const ValueTree& tree); | |||
| virtual const Rectangle<float> 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<float>& targetPositionForOrigin, | |||
| const Point<float>& targetPositionForX1Y0, | |||
| const Point<float>& 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<float>& 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<float>& 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<float>& 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<float> getControlPoint (int index) const; | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| ValueTree createValueTree() const; | |||
| const Rectangle<float> 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 <Drawable> drawables; | |||
| OwnedArray <AffineTransform> transforms; | |||
| Point<float> controlPoints[3]; | |||
| const Rectangle<float> 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<float>& imageTopLeftPosition, | |||
| const Point<float>& imageTopRightPosition, | |||
| const Point<float>& 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<float>& 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<float>& 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<float>& 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<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| static DrawableImage* createFromValueTree (const ValueTree& tree); | |||
| 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<float> 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<float> 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<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| static const Identifier valueTreeType; | |||
| /** @internal */ | |||
| static DrawableText* createFromValueTree (const ValueTree& tree); | |||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||
| juce_UseDebuggingNewOperator | |||
| @@ -88,7 +88,7 @@ public: | |||
| { | |||
| bufferList.calloc (256, 1); | |||
| #ifdef WIN32 | |||
| #if JUCE_WINDOWS | |||
| if (InitializeQTML (0) != noErr) | |||
| return; | |||
| #endif | |||
| @@ -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); | |||
| @@ -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. | |||
| @@ -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 | |||
| @@ -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. | |||
| @@ -895,11 +895,7 @@ Image* ListBox::createSnapshotOfSelectedRows (int& imageX, int& imageY) | |||
| { | |||
| const Point<int> pos (rowComp->relativePositionToOtherComponent (this, Point<int>())); | |||
| const Rectangle<int> rowRect (pos.getX(), pos.getY(), rowComp->getWidth(), rowComp->getHeight()); | |||
| if (imageArea.isEmpty()) | |||
| imageArea = rowRect; | |||
| else | |||
| imageArea = imageArea.getUnion (rowRect); | |||
| imageArea = imageArea.getUnion (rowRect); | |||
| } | |||
| } | |||
| @@ -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() | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -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; | |||
| } | |||
| @@ -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() | |||
| { | |||
| @@ -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; | |||
| }; | |||
| @@ -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); | |||
| if (d == 0) | |||
| { | |||
| d = DrawableComposite::createFromValueTree (tree); | |||
| if (d == 0) | |||
| { | |||
| d = DrawableImage::createFromValueTree (tree); | |||
| if (d == 0) | |||
| d = DrawableText::createFromValueTree (tree); | |||
| } | |||
| } | |||
| const Identifier type (tree.getType()); | |||
| 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->refreshFromValueTree (tree, imageProvider); | |||
| return d; | |||
| } | |||
| @@ -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, 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. | |||
| */ | |||
| static Drawable* createFromValueTree (const ValueTree& tree); | |||
| virtual const Rectangle<float> 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; | |||
| }; | |||
| @@ -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<float>& targetPositionForOrigin, | |||
| const Point<float>& targetPositionForX1Y0, | |||
| const Point<float>& 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<float> DrawableComposite::getBounds() const | |||
| const Rectangle<float> DrawableComposite::getUntransformedBounds() const | |||
| { | |||
| Rectangle<float> 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<float> 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; | |||
| } | |||
| bool DrawableComposite::hitTest (float x, float y) const | |||
| const Rectangle<float> DrawableComposite::getBounds() const | |||
| { | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| { | |||
| float tx = x; | |||
| float ty = y; | |||
| const AffineTransform* const t = transforms.getUnchecked(i); | |||
| return getUntransformedBounds().transformed (getTransform()); | |||
| } | |||
| if (t != 0) | |||
| t->inverted().transformPoint (tx, ty); | |||
| bool DrawableComposite::hitTest (float x, float y) const | |||
| { | |||
| getTransform().inverted().transformPoint (x, y); | |||
| if (drawables.getUnchecked(i)->hitTest (tx, ty)) | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| if (drawables.getUnchecked(i)->hitTest (x, y)) | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| @@ -171,87 +151,123 @@ Drawable* DrawableComposite::createCopy() const | |||
| { | |||
| DrawableComposite* const dc = new DrawableComposite(); | |||
| for (int i = 0; i < 3; ++i) | |||
| dc->controlPoints[i] = controlPoints[i]; | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| { | |||
| dc->drawables.add (drawables.getUnchecked(i)->createCopy()); | |||
| const AffineTransform* const t = transforms.getUnchecked(i); | |||
| dc->transforms.add (t != 0 ? new AffineTransform (*t) : 0); | |||
| } | |||
| return dc; | |||
| } | |||
| //============================================================================== | |||
| ValueTree DrawableComposite::createValueTree() const | |||
| { | |||
| ValueTree v ("Group"); | |||
| const Identifier DrawableComposite::valueTreeType ("Group"); | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty ("id", getName(), 0); | |||
| namespace DrawableCompositeHelpers | |||
| { | |||
| static const Identifier topLeft ("topLeft"); | |||
| static const Identifier topRight ("topRight"); | |||
| static const Identifier bottomLeft ("bottomLeft"); | |||
| for (int i = 0; i < drawables.size(); ++i) | |||
| static void stringToPoint (const String& coords, Point<float>& 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<float>& point) | |||
| { | |||
| return String (point.getX()) + ", " + String (point.getY()); | |||
| } | |||
| } | |||
| DrawableComposite* DrawableComposite::createFromValueTree (const ValueTree& tree) | |||
| const Rectangle<float> DrawableComposite::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| if (! tree.hasType ("Group")) | |||
| return 0; | |||
| jassert (tree.hasType (valueTreeType)); | |||
| Rectangle<float> damageRect; | |||
| DrawableComposite* dc = new DrawableComposite(); | |||
| dc->setName (tree ["id"]); | |||
| setName (tree [idProperty]); | |||
| Point<float> 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; | |||
| } | |||
| @@ -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<float>& targetPositionForOrigin, | |||
| const Point<float>& targetPositionForX1Y0, | |||
| const Point<float>& 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<float>& 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<float>& 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<float>& 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<float> getControlPoint (int index) const; | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| ValueTree createValueTree() const; | |||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||
| /** @internal */ | |||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||
| /** @internal */ | |||
| static DrawableComposite* createFromValueTree (const ValueTree& tree); | |||
| static const Identifier valueTreeType; | |||
| /** @internal */ | |||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| OwnedArray <Drawable> drawables; | |||
| OwnedArray <AffineTransform> transforms; | |||
| Point<float> controlPoints[3]; | |||
| const Rectangle<float> getUntransformedBounds() const; | |||
| const AffineTransform getTransform() const; | |||
| DrawableComposite (const DrawableComposite&); | |||
| DrawableComposite& operator= (const DrawableComposite&); | |||
| @@ -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<float>& imageTopLeftPosition, | |||
| const Point<float>& imageTopRightPosition, | |||
| const Point<float>& imageBottomLeftPosition) | |||
| { | |||
| controlPoints[0] = imageTopLeftPosition; | |||
| controlPoints[1] = imageTopRightPosition; | |||
| controlPoints[2] = imageBottomLeftPosition; | |||
| } | |||
| //============================================================================== | |||
| const AffineTransform DrawableImage::getTransform() const | |||
| { | |||
| if (image == 0) | |||
| return AffineTransform::identity; | |||
| const Point<float> tr (controlPoints[0] + (controlPoints[1] - controlPoints[0]) / image->getWidth()); | |||
| const Point<float> 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<float> DrawableImage::getBounds() const | |||
| if (image == 0) | |||
| return Rectangle<float>(); | |||
| return Rectangle<float> (0, 0, (float) image->getWidth(), (float) image->getHeight()); | |||
| const Point<float> 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<float> (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,59 +202,110 @@ 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<float>& point) | |||
| { | |||
| if (coords.isNotEmpty()) | |||
| { | |||
| const int comma = coords.indexOfChar (','); | |||
| point.setXY (coords.substring (0, comma).getFloatValue(), | |||
| coords.substring (comma).getFloatValue()); | |||
| } | |||
| } | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty ("id", getName(), 0); | |||
| static const var pointToString (const Point<float>& point) | |||
| { | |||
| return String (point.getX()) + ", " + String (point.getY()); | |||
| } | |||
| } | |||
| if (opacity < 1.0f) | |||
| v.setProperty ("opacity", (double) opacity, 0); | |||
| const Rectangle<float> DrawableImage::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| jassert (tree.hasType (valueTreeType)); | |||
| if (! overlayColour.isTransparent()) | |||
| v.setProperty ("overlay", String::toHexString ((int) overlayColour.getARGB()), 0); | |||
| setName (tree [idProperty]); | |||
| if (image != 0) | |||
| 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()) | |||
| { | |||
| 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! | |||
| if (imageProvider != 0) | |||
| newImage = imageProvider->getImageForIdentifier (imageIdentifier); | |||
| } | |||
| Point<float> 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]); | |||
| for (int i = (base64.length() & ~127); i >= 0; i -= 128) | |||
| base64 = base64.substring (0, i) + "\n" + base64.substring (i); | |||
| 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]; | |||
| v.setProperty ("data", base64, 0); | |||
| if (image != newImage) | |||
| { | |||
| ImageCache::release (image); | |||
| image = newImage; | |||
| } | |||
| return getBounds(); | |||
| } | |||
| return v; | |||
| ImageCache::release (newImage); | |||
| return Rectangle<float>(); | |||
| } | |||
| DrawableImage* DrawableImage::createFromValueTree (const ValueTree& tree) | |||
| const ValueTree DrawableImage::createValueTree (ImageProvider* imageProvider) const | |||
| { | |||
| if (! tree.hasType ("Image")) | |||
| return 0; | |||
| ValueTree v (valueTreeType); | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty (idProperty, getName(), 0); | |||
| if (opacity < 1.0f) | |||
| v.setProperty (DrawableImageHelpers::opacity, (double) opacity, 0); | |||
| DrawableImage* di = new DrawableImage(); | |||
| if (! overlayColour.isTransparent()) | |||
| v.setProperty (DrawableImageHelpers::overlay, String::toHexString ((int) overlayColour.getARGB()), 0); | |||
| di->setName (tree ["id"]); | |||
| di->opacity = tree.hasProperty ("opacity") ? (float) tree ["opacity"] : 1.0f; | |||
| di->overlayColour = Colour (tree ["overlay"].toString().getHexValue32()); | |||
| 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); | |||
| } | |||
| MemoryBlock imageData; | |||
| if (imageData.fromBase64Encoding (tree ["data"])) | |||
| if (image != 0) | |||
| { | |||
| 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) | |||
| v.setProperty (DrawableImageHelpers::image, imageProvider->getIdentifierForImage (image), 0); | |||
| } | |||
| return di; | |||
| return v; | |||
| } | |||
| @@ -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<float>& imageTopLeftPosition, | |||
| const Point<float>& imageTopRightPosition, | |||
| const Point<float>& 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<float>& 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<float>& 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<float>& getTargetPositionForBottomLeft() const throw() { return controlPoints[2]; } | |||
| //============================================================================== | |||
| /** @internal */ | |||
| @@ -108,9 +135,13 @@ public: | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| ValueTree createValueTree() const; | |||
| const Rectangle<float> 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<float> controlPoints[3]; | |||
| const AffineTransform getTransform() const; | |||
| DrawableImage (const DrawableImage&); | |||
| DrawableImage& operator= (const DrawableImage&); | |||
| @@ -129,118 +129,158 @@ Drawable* DrawablePath::createCopy() const | |||
| } | |||
| //============================================================================== | |||
| static const FillType readFillTypeFromTree (const ValueTree& v) | |||
| { | |||
| const String type (v["type"].toString()); | |||
| const Identifier DrawablePath::valueTreeType ("Path"); | |||
| if (type.equalsIgnoreCase ("solid")) | |||
| namespace DrawablePathHelpers | |||
| { | |||
| 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"); | |||
| static bool updateFillType (const ValueTree& v, FillType& fillType) | |||
| { | |||
| const String colour (v ["colour"].toString()); | |||
| return Colour (colour.isEmpty() ? (uint32) 0xff000000 | |||
| : (uint32) colour.getHexValue32()); | |||
| const String type (v[type].toString()); | |||
| if (type.equalsIgnoreCase (solid)) | |||
| { | |||
| const String colourString (v [colour].toString()); | |||
| const Colour newColour (colourString.isEmpty() ? (uint32) 0xff000000 | |||
| : (uint32) colourString.getHexValue32()); | |||
| if (fillType.isColour() && fillType.colour == newColour) | |||
| return false; | |||
| 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; | |||
| } | |||
| 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 colours; | |||
| colours.addTokens (v["colours"].toString(), false); | |||
| for (int i = 0; i < colours.size() / 2; ++i) | |||
| g.addColour (colours[i * 2].getDoubleValue(), | |||
| Colour ((uint32) colours[i * 2 + 1].getHexValue32())); | |||
| return g; | |||
| 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; | |||
| } | |||
| jassertfalse; | |||
| return FillType(); | |||
| } | |||
| static ValueTree createTreeForFillType (const String& tagName, const FillType& fillType) | |||
| const Rectangle<float> 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); | |||
| 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 | |||
| } | |||
| Rectangle<float> damageRect; | |||
| setName (tree [idProperty]); | |||
| return v; | |||
| } | |||
| bool needsRedraw = DrawablePathHelpers::updateFillType (tree.getChildWithName (DrawablePathHelpers::fill), mainFill); | |||
| needsRedraw = DrawablePathHelpers::updateFillType (tree.getChildWithName (DrawablePathHelpers::stroke), strokeFill) || needsRedraw; | |||
| ValueTree DrawablePath::createValueTree() const | |||
| { | |||
| ValueTree v ("Path"); | |||
| const String jointStyle (tree [DrawablePathHelpers::jointStyle].toString()); | |||
| const String endStyle (tree [DrawablePathHelpers::capStyle].toString()); | |||
| v.addChild (createTreeForFillType ("fill", mainFill), -1, 0); | |||
| v.addChild (createTreeForFillType ("stroke", strokeFill), -1, 0); | |||
| 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)); | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty ("id", getName(), 0); | |||
| Path newPath; | |||
| newPath.restoreFromString (tree [DrawablePathHelpers::path]); | |||
| 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); | |||
| if (strokeType != newStroke || path != newPath) | |||
| { | |||
| damageRect = getBounds(); | |||
| path.swapWithPath (newPath); | |||
| strokeType = newStroke; | |||
| needsRedraw = true; | |||
| } | |||
| return v; | |||
| if (needsRedraw) | |||
| damageRect = damageRect.getUnion (getBounds()); | |||
| return damageRect; | |||
| } | |||
| DrawablePath* DrawablePath::createFromValueTree (const ValueTree& tree) | |||
| const ValueTree DrawablePath::createValueTree (ImageProvider* imageProvider) const | |||
| { | |||
| if (! tree.hasType ("Path")) | |||
| return 0; | |||
| DrawablePath* p = new DrawablePath(); | |||
| ValueTree v (valueTreeType); | |||
| p->setName (tree ["id"]); | |||
| p->mainFill = readFillTypeFromTree (tree.getChildWithName ("fill")); | |||
| p->strokeFill = readFillTypeFromTree (tree.getChildWithName ("stroke")); | |||
| v.addChild (DrawablePathHelpers::createFillType (DrawablePathHelpers::fill, mainFill), -1, 0); | |||
| v.addChild (DrawablePathHelpers::createFillType (DrawablePathHelpers::stroke, strokeFill), -1, 0); | |||
| 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)); | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty (idProperty, getName(), 0); | |||
| p->path.clear(); | |||
| p->path.restoreFromString (tree ["path"]); | |||
| p->updateOutline(); | |||
| 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 p; | |||
| return v; | |||
| } | |||
| @@ -107,9 +107,13 @@ public: | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| ValueTree createValueTree() const; | |||
| const Rectangle<float> 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 | |||
| @@ -85,29 +85,27 @@ Drawable* DrawableText::createCopy() const | |||
| } | |||
| //============================================================================== | |||
| ValueTree DrawableText::createValueTree() const | |||
| { | |||
| ValueTree v ("Text"); | |||
| const Identifier DrawableText::valueTreeType ("Text"); | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty ("id", getName(), 0); | |||
| const Rectangle<float> DrawableText::refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider) | |||
| { | |||
| jassert (tree.hasType (valueTreeType)); | |||
| setName (tree [idProperty]); | |||
| jassertfalse; // xxx not finished! | |||
| return v; | |||
| return Rectangle<float>(); | |||
| } | |||
| DrawableText* DrawableText::createFromValueTree (const ValueTree& tree) | |||
| const ValueTree DrawableText::createValueTree (ImageProvider* imageProvider) const | |||
| { | |||
| if (! tree.hasType ("Text")) | |||
| return 0; | |||
| ValueTree v (valueTreeType); | |||
| DrawableText* dt = new DrawableText(); | |||
| dt->setName (tree ["id"]); | |||
| if (getName().isNotEmpty()) | |||
| v.setProperty (idProperty, getName(), 0); | |||
| jassertfalse; // xxx not finished! | |||
| return dt; | |||
| return v; | |||
| } | |||
| @@ -78,9 +78,13 @@ public: | |||
| /** @internal */ | |||
| Drawable* createCopy() const; | |||
| /** @internal */ | |||
| ValueTree createValueTree() const; | |||
| const Rectangle<float> 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 | |||
| @@ -615,24 +615,13 @@ const Rectangle<float> GlyphArrangement::getBoundingBox (int startIndex, int num | |||
| num = glyphs.size() - startIndex; | |||
| Rectangle<float> 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; | |||
| @@ -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) | |||
| @@ -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(); | |||
| @@ -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 <size_t> (numElements, other.numElements); | |||
| @@ -204,7 +221,6 @@ const Rectangle<float> Path::getBounds() const throw() | |||
| pathYMax - pathYMin); | |||
| } | |||
| const Rectangle<float> Path::getBoundsTransformed (const AffineTransform& transform) const throw() | |||
| { | |||
| return getBounds().transformed (transform); | |||
| @@ -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. | |||
| @@ -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<float> object. */ | |||
| const Point<float> toFloat() const throw() { return Point<float> (static_cast <float> (x), static_cast<float> (y)); } | |||
| /** Returns the point as a string in the form "x, y". */ | |||
| const String toString() const { return String (x) + ", " + String (y); } | |||
| @@ -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); | |||
| @@ -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) | |||
| { | |||
| @@ -36,81 +36,24 @@ struct CallbackMessagePayload | |||
| }; | |||
| END_JUCE_NAMESPACE | |||
| using namespace JUCE_NAMESPACE; | |||
| @interface JuceAppDelegate : NSObject <UIApplicationDelegate> | |||
| //============================================================================== | |||
| @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]; | |||
| @end | |||
| 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 <void*, CriticalSection>* 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; | |||
| } | |||