| @@ -73,6 +73,7 @@ namespace Ids | |||||
| DECLARE_ID (font); | DECLARE_ID (font); | ||||
| DECLARE_ID (mode); | DECLARE_ID (mode); | ||||
| DECLARE_ID (type); | DECLARE_ID (type); | ||||
| DECLARE_ID (version); | |||||
| DECLARE_ID (position); | DECLARE_ID (position); | ||||
| DECLARE_ID (source); | DECLARE_ID (source); | ||||
| DECLARE_ID (readOnly); | DECLARE_ID (readOnly); | ||||
| @@ -85,6 +86,9 @@ namespace Ids | |||||
| DECLARE_ID (noItemsText); | DECLARE_ID (noItemsText); | ||||
| DECLARE_ID (min); | DECLARE_ID (min); | ||||
| DECLARE_ID (max); | DECLARE_ID (max); | ||||
| DECLARE_ID (width); | |||||
| DECLARE_ID (height); | |||||
| DECLARE_ID (background); | |||||
| DECLARE_ID (interval); | DECLARE_ID (interval); | ||||
| DECLARE_ID (textBoxPos); | DECLARE_ID (textBoxPos); | ||||
| DECLARE_ID (textBoxWidth); | DECLARE_ID (textBoxWidth); | ||||
| @@ -102,6 +106,34 @@ namespace Ids | |||||
| DECLARE_ID (connectedRight); | DECLARE_ID (connectedRight); | ||||
| DECLARE_ID (connectedTop); | DECLARE_ID (connectedTop); | ||||
| DECLARE_ID (connectedBottom); | 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 class_ ("class"); | ||||
| const Identifier id_ ("id"); | const Identifier id_ ("id"); | ||||
| } | } | ||||
| @@ -76,7 +76,7 @@ public: | |||||
| { | { | ||||
| RectangleCoordinates r (sourceValue.toString()); | RectangleCoordinates r (sourceValue.toString()); | ||||
| Coordinate& coord = getCoord (r); | Coordinate& coord = getCoord (r); | ||||
| coord = Coordinate (newValue.toString(), coord.isHorizontal()); | |||||
| coord = Coordinate (newValue.toString(), type == left || type == right); | |||||
| const String newVal (r.toString()); | const String newVal (r.toString()); | ||||
| if (sourceValue != newVal) | if (sourceValue != newVal) | ||||
| @@ -123,12 +123,12 @@ public: | |||||
| Coordinate coord (getCoordinate()); | Coordinate coord (getCoordinate()); | ||||
| PopupMenu m; | 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); | const int r = m.showAt (button); | ||||
| if (r > 0) | if (r > 0) | ||||
| return document.getChosenMarkerMenuItem (compState, coord, r); | |||||
| return document.getChosenMarkerMenuItem (compState, coord, r, type == left || type == right); | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| @@ -142,10 +142,10 @@ private: | |||||
| { | { | ||||
| switch (type) | 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; | default: jassertfalse; break; | ||||
| } | } | ||||
| @@ -181,7 +181,7 @@ Component* ComponentTypeManager::createFromStoredType (ComponentDocument& docume | |||||
| return c; | return c; | ||||
| } | } | ||||
| ComponentTypeHandler* ComponentTypeManager::getHandlerFor (const String& type) | |||||
| ComponentTypeHandler* ComponentTypeManager::getHandlerFor (const Identifier& type) | |||||
| { | { | ||||
| for (int i = handlers.size(); --i >= 0;) | for (int i = handlers.size(); --i >= 0;) | ||||
| if (handlers.getUnchecked(i)->getXmlTag() == type) | if (handlers.getUnchecked(i)->getXmlTag() == type) | ||||
| @@ -248,7 +248,7 @@ public: | |||||
| private: | private: | ||||
| Value sourceValue; | Value sourceValue; | ||||
| ComponentTypeInstance& item; | |||||
| ComponentTypeInstance item; | |||||
| CompMemberNameValueSource (const CompMemberNameValueSource&); | CompMemberNameValueSource (const CompMemberNameValueSource&); | ||||
| const CompMemberNameValueSource& operator= (const CompMemberNameValueSource&); | const CompMemberNameValueSource& operator= (const CompMemberNameValueSource&); | ||||
| @@ -130,7 +130,7 @@ public: | |||||
| int getNumHandlers() const { return handlers.size(); } | int getNumHandlers() const { return handlers.size(); } | ||||
| ComponentTypeHandler* getHandler (const int index) const { return handlers[index]; } | ComponentTypeHandler* getHandler (const int index) const { return handlers[index]; } | ||||
| ComponentTypeHandler* getHandlerFor (const String& type); | |||||
| ComponentTypeHandler* getHandlerFor (const Identifier& type); | |||||
| const StringArray getDisplayNames() const; | const StringArray getDisplayNames() const; | ||||
| private: | private: | ||||
| @@ -71,7 +71,7 @@ public: | |||||
| props.add (new TextPropertyComponent (item.getValue (Ids::text), "Text", 16384, true)); | props.add (new TextPropertyComponent (item.getValue (Ids::text), "Text", 16384, true)); | ||||
| props.getLast()->setTooltip ("The label's text."); | 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 char* const editModes[] = { "Read-only", "Edit on Single-Click", "Edit on Double-Click", 0 }; | ||||
| const int values[] = { 1, 2, 3, 0 }; | const int values[] = { 1, 2, 3, 0 }; | ||||
| @@ -93,7 +93,7 @@ public: | |||||
| << item.getMemberName() << "->setEditable (" << CodeHelpers::boolLiteral (editMode == 2) | << item.getMemberName() << "->setEditable (" << CodeHelpers::boolLiteral (editMode == 2) | ||||
| << ", " << CodeHelpers::boolLiteral (editMode == 3) << ", false);" << newLine; | << ", " << CodeHelpers::boolLiteral (editMode == 3) << ", false);" << newLine; | ||||
| Justification justification ((int) item ["textJustification"]); | |||||
| Justification justification ((int) item [Ids::justification]); | |||||
| if (justification.getFlags() != 0) | if (justification.getFlags() != 0) | ||||
| code.constructorCode << item.getMemberName() << "->setJustificationType (" | code.constructorCode << item.getMemberName() << "->setJustificationType (" | ||||
| << CodeHelpers::justificationToCode (justification) << ");" << newLine; | << 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 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 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)); | props.add (new TextPropertyComponent (Value (new NumericValueSource<double> (item.getValue (Ids::skew))), "Skew Factor", 16, false)); | ||||
| addEditableColourProperties (item, props); | addEditableColourProperties (item, props); | ||||
| @@ -311,30 +311,30 @@ void ComponentDocument::checkRootObject() | |||||
| if ((int) getCanvasHeight().getValue() <= 0) | if ((int) getCanvasHeight().getValue() <= 0) | ||||
| getCanvasHeight() = 480; | getCanvasHeight() = 480; | ||||
| if (! root.hasProperty ("background")) | |||||
| if (! root.hasProperty (Ids::background)) | |||||
| getBackgroundColour() = Colours::white.toString(); | getBackgroundColour() = Colours::white.toString(); | ||||
| } | } | ||||
| void ComponentDocument::setUsingTemporaryCanvasSize (bool b) | 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; | usingTemporaryCanvasSize = b; | ||||
| } | } | ||||
| Value ComponentDocument::getCanvasWidth() const | Value ComponentDocument::getCanvasWidth() const | ||||
| { | { | ||||
| return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable ("width"); | |||||
| return usingTemporaryCanvasSize ? tempCanvasWidth : getRootValueNonUndoable (Ids::width); | |||||
| } | } | ||||
| Value ComponentDocument::getCanvasHeight() const | Value ComponentDocument::getCanvasHeight() const | ||||
| { | { | ||||
| return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable ("height"); | |||||
| return usingTemporaryCanvasSize ? tempCanvasHeight : getRootValueNonUndoable (Ids::height); | |||||
| } | } | ||||
| Value ComponentDocument::getBackgroundColour() const | 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 | 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); | 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) | 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, | 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, | 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 componentName (componentState [memberNameProperty].toString()); | ||||
| const String fullCoordName (componentName + "." + coordName); | 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(); | 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 | 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(); | 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(); | menu.addSeparator(); | ||||
| const MarkerList& markerList = getMarkerList (coord.isHorizontal()); | |||||
| const MarkerList& markerList = getMarkerList (isHorizontal); | |||||
| int i; | int i; | ||||
| for (i = 0; i < markerList.size(); ++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(); | menu.addSeparator(); | ||||
| for (i = 0; i < getNumComponents(); ++i) | for (i = 0; i < getNumComponents(); ++i) | ||||
| @@ -582,30 +592,30 @@ void ComponentDocument::addComponentMarkerMenuItems (const ValueTree& componentS | |||||
| if (compName != componentName) | 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 | 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()); | 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) | if (i >= 100 && i < 10000) | ||||
| return markerList.getName (markerList.getMarker (i - 100)); | return markerList.getName (markerList.getMarker (i - 100)); | ||||
| @@ -707,49 +717,50 @@ void ComponentDocument::MarkerList::renameAnchor (const String& oldName, const S | |||||
| document.renameAnchor (oldName, newName); | 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) | void ComponentDocument::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()) | |||||
| 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 | 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(); | menu.addSeparator(); | ||||
| const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); | |||||
| const MarkerList& markerList = document.getMarkerList (isX); | |||||
| for (int i = 0; i < markerList.size(); ++i) | 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 | 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) | if (i >= 100 && i < 10000) | ||||
| return markerList.getName (markerList.getMarker (i - 100)); | return markerList.getName (markerList.getMarker (i - 100)); | ||||
| @@ -35,7 +35,7 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| class ComponentDocument : public ValueTree::Listener, | class ComponentDocument : public ValueTree::Listener, | ||||
| public Coordinate::MarkerResolver | |||||
| public Coordinate::NamedCoordinateFinder | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -57,8 +57,8 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| const String getUniqueId() const { return root [idProperty]; } | 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); | void setUsingTemporaryCanvasSize (bool b); | ||||
| Value getCanvasWidth() const; | Value getCanvasWidth() const; | ||||
| @@ -84,12 +84,12 @@ public: | |||||
| bool setCoordsFor (ValueTree& componentState, const RectangleCoordinates& newSize); | bool setCoordsFor (ValueTree& componentState, const RectangleCoordinates& newSize); | ||||
| void renameAnchor (const String& oldName, const String& newName); | 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, | 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; | void addNewComponentMenuItems (PopupMenu& menu) const; | ||||
| const ValueTree performNewComponentMenuItem (int menuResultCode); | const ValueTree performNewComponentMenuItem (int menuResultCode); | ||||
| @@ -105,7 +105,7 @@ public: | |||||
| public: | public: | ||||
| MarkerList (ComponentDocument& document, bool isX); | 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); | bool createProperties (Array <PropertyComponent*>& props, const String& itemId); | ||||
| void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); | void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); | ||||
| const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; | const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; | ||||
| @@ -181,8 +181,8 @@ private: | |||||
| void checkRootObject(); | void checkRootObject(); | ||||
| void createSubTreeIfNotThere (const Identifier& name); | 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 getRootValueUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, getUndoManager()); } | ||||
| Value getRootValueNonUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, 0); } | 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_) | DrawableDocument::DrawableDocument (Project* project_) | ||||
| : project (project_), | : project (project_), | ||||
| root (drawableTag), | |||||
| root (Tags::drawableTag), | |||||
| saveAsXml (true), | saveAsXml (true), | ||||
| needsSaving (false) | needsSaving (false) | ||||
| { | { | ||||
| @@ -166,7 +168,7 @@ bool DrawableDocument::load (InputStream& input) | |||||
| loadedTree = ValueTree::readFromStream (input); | loadedTree = ValueTree::readFromStream (input); | ||||
| } | } | ||||
| if (loadedTree.hasType (drawableTag)) | |||||
| if (loadedTree.hasType (Tags::drawableTag)) | |||||
| { | { | ||||
| addMissingIds (loadedTree); | 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_) | 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_) | 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) | bool DrawableDocument::MarkerList::createProperties (Array <PropertyComponent*>& props, const String& itemId) | ||||
| @@ -334,10 +341,10 @@ bool DrawableDocument::MarkerList::createProperties (Array <PropertyComponent*>& | |||||
| return false; | 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) | bool isAnchor1, const String& fullCoordName) | ||||
| { | { | ||||
| Coordinate requestedCoord (findMarker (name, coord.isHorizontal())); | |||||
| // Coordinate requestedCoord (findNamedCoordinate (objectName, edge, coord.isHorizontal())); | |||||
| // menu.addItem (i, name, | // menu.addItem (i, name, | ||||
| // ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)), | // ! (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) | 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()) | 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 | 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(); | menu.addSeparator(); | ||||
| const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); | const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); | ||||
| for (int i = 0; i < markerList.size(); ++i) | 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 | 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()); | const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); | ||||
| if (i >= 100 && i < 10000) | if (i >= 100 && i < 10000) | ||||
| return markerList.getName (markerList.getMarker (i - 100)); | return markerList.getName (markerList.getMarker (i - 100)); | ||||
| jassertfalse; | |||||
| jassertfalse;*/ | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| @@ -57,8 +57,8 @@ public: | |||||
| void addCircle(); | void addCircle(); | ||||
| void addImage (const File& imageFile); | 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); | static const String getIdFor (const ValueTree& object); | ||||
| @@ -69,7 +69,7 @@ public: | |||||
| MarkerList (DrawableDocument& document, bool isX); | MarkerList (DrawableDocument& document, bool isX); | ||||
| ~MarkerList() {} | ~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); | bool createProperties (Array <PropertyComponent*>& props, const String& itemId); | ||||
| void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); | void addMarkerMenuItems (const ValueTree& markerState, const Coordinate& coord, PopupMenu& menu, bool isAnchor1); | ||||
| const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; | const String getChosenMarkerMenuItem (const Coordinate& coord, int itemId) const; | ||||
| @@ -117,9 +117,9 @@ private: | |||||
| void addMissingIds (ValueTree tree) const; | void addMissingIds (ValueTree tree) const; | ||||
| void addDrawable (Drawable& d); | 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 configurations ("CONFIGURATIONS"); | ||||
| const Identifier configuration ("CONFIGURATION"); | const Identifier configuration ("CONFIGURATION"); | ||||
| const Identifier exporters ("EXPORTFORMATS"); | const Identifier exporters ("EXPORTFORMATS"); | ||||
| const Identifier configGroup ("JUCEOPTIONS"); | |||||
| } | } | ||||
| const char* Project::projectFileExtension = ".jucer"; | const char* Project::projectFileExtension = ".jucer"; | ||||
| @@ -80,7 +81,7 @@ const String Project::getDocumentTitle() | |||||
| void Project::updateProjectSettings() | void Project::updateProjectSettings() | ||||
| { | { | ||||
| projectRoot.setProperty ("jucerVersion", ProjectInfo::versionString, 0); | |||||
| projectRoot.setProperty (Ids::jucerVersion, ProjectInfo::versionString, 0); | |||||
| projectRoot.setProperty (Ids::name, getDocumentTitle(), 0); | projectRoot.setProperty (Ids::name, getDocumentTitle(), 0); | ||||
| } | } | ||||
| @@ -101,13 +102,13 @@ void Project::setMissingDefaultValues() | |||||
| if (getDocumentTitle().isEmpty()) | if (getDocumentTitle().isEmpty()) | ||||
| setTitle ("Juce Project"); | setTitle ("Juce Project"); | ||||
| if (! projectRoot.hasProperty ("projectType")) | |||||
| if (! projectRoot.hasProperty (Ids::projectType)) | |||||
| getProjectType() = (int) application; | getProjectType() = (int) application; | ||||
| if (! projectRoot.hasProperty ("version")) | |||||
| if (! projectRoot.hasProperty (Ids::version)) | |||||
| getVersion() = "1.0.0"; | getVersion() = "1.0.0"; | ||||
| if (! projectRoot.hasProperty ("juceLinkage")) | |||||
| if (! projectRoot.hasProperty (Ids::juceLinkage)) | |||||
| getJuceLinkageModeValue() = useAmalgamatedJuceViaMultipleTemplates; | getJuceLinkageModeValue() = useAmalgamatedJuceViaMultipleTemplates; | ||||
| const String juceFolderPath (getRelativePathForFile (StoredSettings::getInstance()->getLastKnownJuceFolder())); | const String juceFolderPath (getRelativePathForFile (StoredSettings::getInstance()->getLastKnownJuceFolder())); | ||||
| @@ -124,7 +125,7 @@ void Project::setMissingDefaultValues() | |||||
| const String sanitisedProjectName (CodeHelpers::makeValidIdentifier (getProjectName().toString(), false, true, false)); | const String sanitisedProjectName (CodeHelpers::makeValidIdentifier (getProjectName().toString(), false, true, false)); | ||||
| if (! projectRoot.hasProperty ("buildVST")) | |||||
| if (! projectRoot.hasProperty (Ids::buildVST)) | |||||
| { | { | ||||
| shouldBuildVST() = true; | shouldBuildVST() = true; | ||||
| shouldBuildRTAS() = false; | shouldBuildRTAS() = false; | ||||
| @@ -147,7 +148,7 @@ void Project::setMissingDefaultValues() | |||||
| getPluginRTASCategory() = String::empty; | getPluginRTASCategory() = String::empty; | ||||
| } | } | ||||
| if (! projectRoot.hasProperty ("bundleIdentifier")) | |||||
| if (! projectRoot.hasProperty (Ids::bundleIdentifier)) | |||||
| setBundleIdentifierToDefault(); | setBundleIdentifierToDefault(); | ||||
| } | } | ||||
| @@ -473,7 +474,7 @@ bool Project::Item::shouldBeCompiled() const | |||||
| Value Project::Item::getShouldCompileValue() const | Value Project::Item::getShouldCompileValue() const | ||||
| { | { | ||||
| return node.getPropertyAsValue ("compile", getUndoManager()); | |||||
| return node.getPropertyAsValue (Ids::compile, getUndoManager()); | |||||
| } | } | ||||
| bool Project::Item::shouldBeAddedToBinaryResources() const | bool Project::Item::shouldBeAddedToBinaryResources() const | ||||
| @@ -483,7 +484,7 @@ bool Project::Item::shouldBeAddedToBinaryResources() const | |||||
| Value Project::Item::getShouldAddToResourceValue() const | Value Project::Item::getShouldAddToResourceValue() const | ||||
| { | { | ||||
| return node.getPropertyAsValue ("resource", getUndoManager()); | |||||
| return node.getPropertyAsValue (Ids::resource, getUndoManager()); | |||||
| } | } | ||||
| const File Project::Item::getFile() const | const File Project::Item::getFile() const | ||||
| @@ -673,11 +674,11 @@ Image* Project::Item::getIcon() const | |||||
| //============================================================================== | //============================================================================== | ||||
| ValueTree Project::getJuceConfigNode() | ValueTree Project::getJuceConfigNode() | ||||
| { | { | ||||
| ValueTree configNode = projectRoot.getChildWithName ("JUCEOPTIONS"); | |||||
| ValueTree configNode = projectRoot.getChildWithName (Tags::configGroup); | |||||
| if (! configNode.isValid()) | if (! configNode.isValid()) | ||||
| { | { | ||||
| configNode = ValueTree ("JUCEOPTIONS"); | |||||
| configNode = ValueTree (Tags::configGroup); | |||||
| projectRoot.addChild (configNode, -1, 0); | projectRoot.addChild (configNode, -1, 0); | ||||
| } | } | ||||
| @@ -221,24 +221,24 @@ public: | |||||
| void createPropertyEditors (Array <PropertyComponent*>& properties); | 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 | // 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; | const String getGCCOptimisationFlag() const; | ||||
| Value getPreprocessorDefs() const { return getValue ("defines"); } | |||||
| Value getPreprocessorDefs() const { return getValue (Ids::defines); } | |||||
| const StringArray parsePreprocessorDefs() const; | const StringArray parsePreprocessorDefs() const; | ||||
| Value getHeaderSearchPath() const { return getValue ("headerPath"); } | |||||
| Value getHeaderSearchPath() const { return getValue (Ids::headerPath); } | |||||
| const StringArray getHeaderSearchPaths() const; | const StringArray getHeaderSearchPaths() const; | ||||
| static const char* const osxVersionDefault; | static const char* const osxVersionDefault; | ||||
| static const char* const osxVersion10_4; | static const char* const osxVersion10_4; | ||||
| static const char* const osxVersion10_5; | static const char* const osxVersion10_5; | ||||
| static const char* const osxVersion10_6; | 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: | private: | ||||
| @@ -246,7 +246,7 @@ public: | |||||
| Project* project; | Project* project; | ||||
| ValueTree config; | 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); } | UndoManager* getUndoManager() const { return project->getUndoManagerFor (config); } | ||||
| BuildConfiguration (Project* project, const ValueTree& configNode); | BuildConfiguration (Project* project, const ValueTree& configNode); | ||||
| @@ -128,10 +128,10 @@ public: | |||||
| const int libTypeValues[] = { 1, 2, 0 }; | const int libTypeValues[] = { 1, 2, 0 }; | ||||
| props.add (new ChoicePropertyComponent (getLibraryType(), "Library Type", StringArray (libTypes), Array<var> (libTypeValues))); | 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.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."); | 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 getDSPFile() const { return getProjectFile (".dsp"); } | ||||
| const File getDSWFile() const { return getProjectFile (".dsw"); } | 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; } | bool isLibraryDLL() const { return project.isLibrary() && getLibraryType() == 2; } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -438,7 +438,7 @@ private: | |||||
| const String getBinaryFileForConfig (const Project::BuildConfiguration& config) const | 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()) | if (targetBinary.isNotEmpty()) | ||||
| return targetBinary; | return targetBinary; | ||||
| @@ -570,7 +570,7 @@ private: | |||||
| for (int i = 0; i < objects.size(); ++i) | for (int i = 0; i < objects.size(); ++i) | ||||
| { | { | ||||
| ValueTree& o = *objects.getUnchecked(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) | for (int j = 0; j < o.getNumProperties(); ++j) | ||||
| { | { | ||||
| @@ -814,7 +814,7 @@ private: | |||||
| StringArray configIDs; | StringArray configIDs; | ||||
| for (int i = 0; i < configsToUse.size(); ++i) | for (int i = 0; i < configsToUse.size(); ++i) | ||||
| configIDs.add (configsToUse[i]->getType()); | |||||
| configIDs.add (configsToUse[i]->getType().toString()); | |||||
| ValueTree* v = new ValueTree (listID); | ValueTree* v = new ValueTree (listID); | ||||
| v->setProperty ("isa", "XCConfigurationList", 0); | v->setProperty ("isa", "XCConfigurationList", 0); | ||||
| @@ -61,23 +61,23 @@ public: | |||||
| const File getTargetFolder() const; | const File getTargetFolder() const; | ||||
| const ValueTree& getSettings() const { return settings; } | 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 isVST() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildVST().getValue(); } | ||||
| bool isRTAS() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildRTAS().getValue(); } | bool isRTAS() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildRTAS().getValue(); } | ||||
| bool isAU() const { return (bool) project.isAudioPlugin() && (bool) project.shouldBuildAU().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; | const StringArray parsePreprocessorDefs() const; | ||||
| // This adds the quotes, and may return angle-brackets, eg: <foo/bar.h> or normal quotes. | // 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 | const String getExporterIdentifierMacro() const | ||||
| { | { | ||||
| return "JUCER_" + settings.getType() + "_" | return "JUCER_" + settings.getType() + "_" | ||||
| + String::toHexString (settings ["targetFolder"].toString().hashCode()).toUpperCase(); | |||||
| + String::toHexString (settings [Ids::targetFolder].toString().hashCode()).toUpperCase(); | |||||
| } | } | ||||
| Array<RelativePath> juceWrapperFiles; | Array<RelativePath> juceWrapperFiles; | ||||
| @@ -91,9 +91,9 @@ public: | |||||
| return editor.getSelection(); | return editor.getSelection(); | ||||
| } | } | ||||
| void getSelectedItemProperties (Array<PropertyComponent*>& newComps) | |||||
| void getSelectedItemProperties (Array<PropertyComponent*>& props) | |||||
| { | { | ||||
| editor.getSelectedItemProperties (newComps); | |||||
| editor.getDocument().createItemProperties (props, editor.getSelectedIds()); | |||||
| } | } | ||||
| private: | private: | ||||
| @@ -178,11 +178,6 @@ const StringArray ComponentEditor::getSelectedIds() const | |||||
| return ids; | return ids; | ||||
| } | } | ||||
| void ComponentEditor::getSelectedItemProperties (Array <PropertyComponent*>& props) | |||||
| { | |||||
| getDocument().createItemProperties (props, getSelectedIds()); | |||||
| } | |||||
| void ComponentEditor::deleteSelection() | void ComponentEditor::deleteSelection() | ||||
| { | { | ||||
| const StringArray ids (getSelectedIds()); | const StringArray ids (getSelectedIds()); | ||||
| @@ -56,7 +56,6 @@ public: | |||||
| ComponentDocument& getDocument() const { return *componentDocument; } | ComponentDocument& getDocument() const { return *componentDocument; } | ||||
| const StringArray getSelectedIds() const; | const StringArray getSelectedIds() const; | ||||
| void getSelectedItemProperties (Array <PropertyComponent*>& props); | |||||
| void deleteSelection(); | void deleteSelection(); | ||||
| void deselectNonComponents(); | void deselectNonComponents(); | ||||
| void selectionToFront(); | void selectionToFront(); | ||||
| @@ -90,9 +90,9 @@ public: | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| void showPopupMenu (const Point<int>& position) | |||||
| void showPopupMenu (bool isClickOnSelectedObject) | |||||
| { | { | ||||
| if (findObjectIdAt (position).isNotEmpty()) | |||||
| if (isClickOnSelectedObject) | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| m.addCommandItem (commandManager, CommandIDs::toFront); | m.addCommandItem (commandManager, CommandIDs::toFront); | ||||
| @@ -29,101 +29,6 @@ | |||||
| #include "../../utility/jucer_ColourEditorComponent.h" | #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 | class ComponentEditorToolbarFactory : public ToolbarItemFactory | ||||
| { | { | ||||
| @@ -138,12 +43,12 @@ public: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| enum DemoToolbarItemIds | |||||
| enum ToolbarItemIds | |||||
| { | { | ||||
| createComponent = 1, | createComponent = 1, | ||||
| changeBackground, | changeBackground, | ||||
| showInfo, | showInfo, | ||||
| showComponentTree, | |||||
| showTree, | |||||
| showOrHideMarkers, | showOrHideMarkers, | ||||
| toggleSnapping | toggleSnapping | ||||
| }; | }; | ||||
| @@ -153,7 +58,7 @@ public: | |||||
| ids.add (createComponent); | ids.add (createComponent); | ||||
| ids.add (changeBackground); | ids.add (changeBackground); | ||||
| ids.add (showInfo); | ids.add (showInfo); | ||||
| ids.add (showComponentTree); | |||||
| ids.add (showTree); | |||||
| ids.add (showOrHideMarkers); | ids.add (showOrHideMarkers); | ||||
| ids.add (toggleSnapping); | ids.add (toggleSnapping); | ||||
| @@ -171,7 +76,7 @@ public: | |||||
| ids.add (showOrHideMarkers); | ids.add (showOrHideMarkers); | ||||
| ids.add (toggleSnapping); | ids.add (toggleSnapping); | ||||
| ids.add (flexibleSpacerId); | ids.add (flexibleSpacerId); | ||||
| ids.add (showComponentTree); | |||||
| ids.add (showTree); | |||||
| ids.add (showInfo); | ids.add (showInfo); | ||||
| ids.add (spacerId); | ids.add (spacerId); | ||||
| } | } | ||||
| @@ -186,17 +91,56 @@ public: | |||||
| case createComponent: return new NewComponentToolbarButton (editor, itemId); | case createComponent: return new NewComponentToolbarButton (editor, itemId); | ||||
| case changeBackground: return new BackgroundColourToolbarButton (editor, itemId); | case changeBackground: return new BackgroundColourToolbarButton (editor, itemId); | ||||
| case showInfo: name = "info"; commandId = CommandIDs::showOrHideProperties; break; | 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 showOrHideMarkers: name = "markers"; commandId = CommandIDs::showOrHideMarkers; break; | ||||
| case toggleSnapping: name = "snap"; commandId = CommandIDs::toggleSnapping; break; | case toggleSnapping: name = "snap"; commandId = CommandIDs::toggleSnapping; break; | ||||
| default: jassertfalse; return 0; | default: jassertfalse; return 0; | ||||
| } | } | ||||
| JucerToolbarButton* b = new JucerToolbarButton (editor, itemId, name); | |||||
| JucerToolbarButton* b = new JucerToolbarButton (itemId, name); | |||||
| b->setCommandToTrigger (commandManager, commandId, true); | b->setCommandToTrigger (commandManager, commandId, true); | ||||
| return b; | 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: | private: | ||||
| ComponentEditor& editor; | ComponentEditor& editor; | ||||
| @@ -107,7 +107,8 @@ void ComponentViewer::handleAsyncUpdate() | |||||
| componentsInOrder.add (c); | 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.. | // Make sure the z-order is correct.. | ||||
| @@ -53,7 +53,7 @@ public: | |||||
| void createCanvas() | void createCanvas() | ||||
| { | { | ||||
| initialise (new DrawableEditorCanvas (editor), toolbarFactory, | initialise (new DrawableEditorCanvas (editor), toolbarFactory, | ||||
| DrawableTreeViewItem::createItemForNode (editor, editor.getDocument().getRootDrawableNode())); | |||||
| new DrawableTreeViewItem (editor, editor.getDocument().getRootDrawableNode())); | |||||
| } | } | ||||
| SelectedItemSet<String>& getSelection() | SelectedItemSet<String>& getSelection() | ||||
| @@ -101,3 +101,155 @@ void DrawableEditor::resized() | |||||
| { | { | ||||
| panel->setBounds (getLocalBounds()); | 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); | Project* project, DrawableDocument* drawableDocument); | ||||
| ~DrawableEditor(); | ~DrawableEditor(); | ||||
| //============================================================================== | |||||
| void getAllCommands (Array <CommandID>& commands); | |||||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); | |||||
| bool perform (const InvocationInfo& info); | |||||
| void paint (Graphics& g); | void paint (Graphics& g); | ||||
| void resized(); | void resized(); | ||||
| //============================================================================== | |||||
| void deleteSelection(); | |||||
| void selectionToFront(); | |||||
| void selectionToBack(); | |||||
| void showNewShapeMenu (Component* componentToAttachTo); | |||||
| //============================================================================== | |||||
| DrawableDocument& getDocument() const { return *drawableDocument; } | DrawableDocument& getDocument() const { return *drawableDocument; } | ||||
| EditorCanvasBase::SelectedItems& getSelection() { return selection; } | EditorCanvasBase::SelectedItems& getSelection() { return selection; } | ||||
| @@ -74,11 +74,11 @@ public: | |||||
| return String::empty; | return String::empty; | ||||
| } | } | ||||
| void showPopupMenu (const Point<int>& position) | |||||
| void showPopupMenu (bool isClickOnSelectedObject) | |||||
| { | { | ||||
| PopupMenu m; | PopupMenu m; | ||||
| if (findObjectIdAt (position).isNotEmpty()) | |||||
| if (isClickOnSelectedObject) | |||||
| { | { | ||||
| m.addCommandItem (commandManager, CommandIDs::toFront); | m.addCommandItem (commandManager, CommandIDs::toFront); | ||||
| m.addCommandItem (commandManager, CommandIDs::toBack); | 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) | void getAllToolbarItemIds (Array <int>& ids) | ||||
| { | { | ||||
| ids.add (createShape); | |||||
| ids.add (showInfo); | ids.add (showInfo); | ||||
| ids.add (showComponentTree); | |||||
| ids.add (showTree); | |||||
| ids.add (showOrHideMarkers); | |||||
| ids.add (toggleSnapping); | |||||
| ids.add (separatorBarId); | ids.add (separatorBarId); | ||||
| ids.add (spacerId); | ids.add (spacerId); | ||||
| @@ -61,8 +66,12 @@ public: | |||||
| void getDefaultItemSet (Array <int>& ids) | void getDefaultItemSet (Array <int>& ids) | ||||
| { | { | ||||
| ids.add (spacerId); | ids.add (spacerId); | ||||
| ids.add (createShape); | |||||
| ids.add (flexibleSpacerId); | ids.add (flexibleSpacerId); | ||||
| ids.add (showComponentTree); | |||||
| ids.add (showOrHideMarkers); | |||||
| ids.add (toggleSnapping); | |||||
| ids.add (flexibleSpacerId); | |||||
| ids.add (showTree); | |||||
| ids.add (showInfo); | ids.add (showInfo); | ||||
| ids.add (spacerId); | ids.add (spacerId); | ||||
| } | } | ||||
| @@ -74,16 +83,38 @@ public: | |||||
| switch (itemId) | switch (itemId) | ||||
| { | { | ||||
| case createShape: return new NewShapeToolbarButton (editor, itemId); | |||||
| case showInfo: name = "info"; commandId = CommandIDs::showOrHideProperties; break; | 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; | default: jassertfalse; return 0; | ||||
| } | } | ||||
| ToolbarButton* b = new ToolbarButton (itemId, name, new DrawablePath(), 0); | |||||
| JucerToolbarButton* b = new JucerToolbarButton (itemId, name); | |||||
| b->setCommandToTrigger (commandManager, commandId, true); | b->setCommandToTrigger (commandManager, commandId, true); | ||||
| return b; | 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: | private: | ||||
| DrawableEditor& editor; | DrawableEditor& editor; | ||||
| @@ -34,44 +34,14 @@ class DrawableTreeViewItem : public JucerTreeViewBase, | |||||
| public ValueTree::Listener, | public ValueTree::Listener, | ||||
| public ChangeListener | 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); | node.addListener (this); | ||||
| editor.getSelection().addChangeListener (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() | ~DrawableTreeViewItem() | ||||
| { | { | ||||
| editor.getSelection().removeChangeListener (this); | editor.getSelection().removeChangeListener (this); | ||||
| @@ -97,7 +67,8 @@ public: | |||||
| // TreeViewItem stuff.. | // TreeViewItem stuff.. | ||||
| bool mightContainSubItems() | bool mightContainSubItems() | ||||
| { | { | ||||
| return node.getNumChildren() > 0; | |||||
| return node.getType() == DrawableComposite::valueTreeType | |||||
| && node.getNumChildren() > 0; | |||||
| } | } | ||||
| const String getUniqueName() const | const String getUniqueName() const | ||||
| @@ -114,23 +85,24 @@ public: | |||||
| void refreshSubItems() | 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); | addSubItem (item); | ||||
| } | |||||
| } | |||||
| if (oldOpenness != 0) | |||||
| restoreOpennessState (*oldOpenness); | |||||
| if (oldOpenness != 0) | |||||
| restoreOpennessState (*oldOpenness); | |||||
| editor.getSelection().changed(); | |||||
| editor.getSelection().changed(); | |||||
| } | |||||
| } | } | ||||
| const String getDisplayName() const | const String getDisplayName() const | ||||
| @@ -37,7 +37,8 @@ public: | |||||
| : OverlayItemComponent (canvas_), | : OverlayItemComponent (canvas_), | ||||
| objectState (objectState_), | objectState (objectState_), | ||||
| objectId (objectId_), | objectId (objectId_), | ||||
| borderThickness (4) | |||||
| borderThickness (4), | |||||
| isDragging (false) | |||||
| { | { | ||||
| jassert (objectState.isValid()); | jassert (objectState.isValid()); | ||||
| } | } | ||||
| @@ -59,26 +60,50 @@ public: | |||||
| void mouseDown (const MouseEvent& e) | void mouseDown (const MouseEvent& e) | ||||
| { | { | ||||
| updateDragZone (e.getPosition()); | 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) | 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) | 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) | bool hitTest (int x, int y) | ||||
| { | { | ||||
| return ! getCentreArea().contains (x, y); | |||||
| if (ModifierKeys::getCurrentModifiers().isAnyModifierKeyDown()) | |||||
| return ! getCentreArea().contains (x, y); | |||||
| return true; | |||||
| } | } | ||||
| bool updatePosition() | bool updatePosition() | ||||
| @@ -130,7 +155,7 @@ public: | |||||
| void updatePosition (const Rectangle<int>& bounds) | void updatePosition (const Rectangle<int>& bounds) | ||||
| { | { | ||||
| RectangleCoordinates coords (canvas->getObjectCoords (state)); | RectangleCoordinates coords (canvas->getObjectCoords (state)); | ||||
| Coordinate coord (false); | |||||
| Coordinate coord; | |||||
| Rectangle<int> r; | Rectangle<int> r; | ||||
| switch (type) | switch (type) | ||||
| @@ -174,6 +199,7 @@ private: | |||||
| ResizableBorderComponent::Zone dragZone; | ResizableBorderComponent::Zone dragZone; | ||||
| const int borderThickness; | const int borderThickness; | ||||
| OwnedArray <SizeGuideComponent> sizeGuides; | OwnedArray <SizeGuideComponent> sizeGuides; | ||||
| bool isDragging; | |||||
| const Rectangle<int> getCentreArea() const | const Rectangle<int> getCentreArea() const | ||||
| { | { | ||||
| @@ -389,7 +415,7 @@ public: | |||||
| if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse)) | if (underMouse.isNotEmpty() && ! getSelection().isSelected (underMouse)) | ||||
| getSelection().selectOnly (underMouse); | getSelection().selectOnly (underMouse); | ||||
| canvas->showPopupMenu (e2.getPosition()); | |||||
| canvas->showPopupMenu (underMouse.isNotEmpty()); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -72,7 +72,7 @@ public: | |||||
| virtual MarkerListBase& getMarkerList (bool isX) = 0; | virtual MarkerListBase& getMarkerList (bool isX) = 0; | ||||
| virtual const SelectedItems::ItemType findObjectIdAt (const Point<int>& position) = 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 void objectDoubleClicked (const MouseEvent& e, const ValueTree& state) = 0; | ||||
| virtual const ValueTree getObjectState (const String& objectId) = 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() | 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) | if (recursionCounter > 100) | ||||
| { | { | ||||
| jassertfalse | 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 | try | ||||
| { | { | ||||
| return resolve (markerResolver, 0); | |||||
| return resolve (nameSource, 0); | |||||
| } | } | ||||
| catch (RecursivePositionException&) | |||||
| catch (RecursiveCoordinateException&) | |||||
| {} | {} | ||||
| return 0.0; | return 0.0; | ||||
| } | } | ||||
| void Coordinate::moveToAbsolute (double newPos, const MarkerResolver& markerResolver) | |||||
| void Coordinate::moveToAbsolute (double newPos, const NamedCoordinateFinder& nameSource) | |||||
| { | { | ||||
| try | 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) | if (size != 0) | ||||
| value = (newPos - pos1) / size; | value = (newPos - pos1) / size; | ||||
| @@ -143,242 +170,295 @@ void Coordinate::moveToAbsolute (double newPos, const MarkerResolver& markerReso | |||||
| value = newPos - pos1; | 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; | ++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; | ++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; | int i = 0; | ||||
| anchor1 = readMarkerName (s, i); | |||||
| anchor1 = CoordParserHelpers::readAnchorName (s, i); | |||||
| if (anchor1.isNotEmpty()) | if (anchor1.isNotEmpty()) | ||||
| { | { | ||||
| skipWhitespace (s, i); | |||||
| CoordParserHelpers::skipWhitespace (s, i); | |||||
| if (s[i] == '+') | if (s[i] == '+') | ||||
| value = readNumber (s, ++i); | |||||
| value = CoordParserHelpers::readNumber (s, ++i); | |||||
| else if (s[i] == '-') | else if (s[i] == '-') | ||||
| value = -readNumber (s, ++i); | |||||
| value = -CoordParserHelpers::readNumber (s, ++i); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| value = readNumber (s, i); | |||||
| skipWhitespace (s, i); | |||||
| anchor1 = getOriginAnchorName (isHorizontal); | |||||
| value = CoordParserHelpers::readNumber (s, i); | |||||
| CoordParserHelpers::skipWhitespace (s, i); | |||||
| if (s[i] == '%') | if (s[i] == '%') | ||||
| { | { | ||||
| isProportion = true; | |||||
| value /= 100.0; | value /= 100.0; | ||||
| skipWhitespace (s, ++i); | |||||
| CoordParserHelpers::skipWhitespace (s, ++i); | |||||
| if (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] == '>') | if (s[i] == '-' && s[i + 1] == '>') | ||||
| { | { | ||||
| i += 2; | i += 2; | ||||
| anchor2 = readMarkerName (s, i); | |||||
| anchor2 = CoordParserHelpers::readAnchorName (s, i); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| anchor2 = anchor1; | anchor2 = anchor1; | ||||
| anchor1 = getOriginMarkerName(); | |||||
| anchor1 = getOriginAnchorName (isHorizontal); | |||||
| } | } | ||||
| } | } | ||||
| else | 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 | 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 (isOrigin (anchor1)) | ||||
| { | { | ||||
| if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName) | |||||
| if (anchor2 == "parent.right" || anchor2 == "parent.bottom") | |||||
| return percent + "%"; | return percent + "%"; | ||||
| else | else | ||||
| return percent + "% * " + checkName (anchor2); | |||||
| return percent + "% * " + anchor2; | |||||
| } | } | ||||
| else | else | ||||
| return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2); | |||||
| return percent + "% * " + anchor1 + " -> " + anchor2; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (isOrigin (anchor1)) | if (isOrigin (anchor1)) | ||||
| return limitedAccuracyString (value); | |||||
| return CoordParserHelpers::limitedAccuracyString (value); | |||||
| else if (value > 0) | else if (value > 0) | ||||
| return checkName (anchor1) + " + " + limitedAccuracyString (value); | |||||
| return anchor1 + " + " + CoordParserHelpers::limitedAccuracyString (value); | |||||
| else if (value < 0) | else if (value < 0) | ||||
| return checkName (anchor1) + " - " + limitedAccuracyString (-value); | |||||
| return anchor1 + " - " + CoordParserHelpers::limitedAccuracyString (-value); | |||||
| else | 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 (oldName.isNotEmpty()); | ||||
| jassert (newName.toLowerCase().containsOnly ("abcdefghijklmnopqrstuvwxyz0123456789_")); | |||||
| if (newName.isEmpty()) | 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; | anchor1 = String::empty; | ||||
| anchor2 = String::empty; | anchor2 = String::empty; | ||||
| } | } | ||||
| } | } | ||||
| else | 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() | RectangleCoordinates::RectangleCoordinates() | ||||
| : left (true), right (true), top (false), bottom (false) | |||||
| { | { | ||||
| } | } | ||||
| RectangleCoordinates::RectangleCoordinates (const Rectangle<float>& rect, const String& componentName) | RectangleCoordinates::RectangleCoordinates (const Rectangle<float>& rect, const String& componentName) | ||||
| : left (rect.getX(), true), | : left (rect.getX(), true), | ||||
| right (rect.getWidth(), componentName + ".left", true), | |||||
| right (rect.getWidth(), componentName + "." + Coordinate::Strings::left), | |||||
| top (rect.getY(), false), | top (rect.getY(), false), | ||||
| bottom (rect.getHeight(), componentName + ".top", false) | |||||
| bottom (rect.getHeight(), componentName + "." + Coordinate::Strings::top) | |||||
| { | { | ||||
| } | } | ||||
| RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | ||||
| : left (true), right (true), top (false), bottom (false) | |||||
| { | { | ||||
| StringArray tokens; | StringArray tokens; | ||||
| tokens.addTokens (stringVersion, ",", String::empty); | tokens.addTokens (stringVersion, ",", String::empty); | ||||
| @@ -389,22 +469,22 @@ RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | |||||
| bottom = Coordinate (tokens [3], false); | 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); | 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 | const String RectangleCoordinates::toString() const | ||||
| @@ -413,12 +493,12 @@ const String RectangleCoordinates::toString() const | |||||
| } | } | ||||
| void RectangleCoordinates::renameAnchorIfUsed (const String& oldName, const String& newName, | 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(); | 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); | 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); | MarkerPosition* m = markers.getUnchecked(i); | ||||
| if (m->markerName == name) | |||||
| if (m->markerName == objectName) | |||||
| return m->position; | return m->position; | ||||
| } | } | ||||
| return Coordinate (isHorizontal); | |||||
| return Coordinate(); | |||||
| } | } | ||||
| void ComponentAutoLayoutManager::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) | 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 | class Coordinate | ||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a zero coordinate. */ | /** Creates a zero coordinate. */ | ||||
| explicit Coordinate (bool isHorizontal); | |||||
| Coordinate(); | |||||
| /** Recreates a coordinate from its stringified version. */ | /** Recreates a coordinate from its stringified version. */ | ||||
| Coordinate (const String& stringVersion, bool isHorizontal); | Coordinate (const String& stringVersion, bool isHorizontal); | ||||
| @@ -47,99 +47,143 @@ public: | |||||
| /** Creates an absolute position from the parent origin. */ | /** Creates an absolute position from the parent origin. */ | ||||
| Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal); | 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. */ | /** Destructor. */ | ||||
| ~Coordinate(); | ~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: | 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. */ | /** 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. */ | 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. | /** Tells the coord that an anchor is changing its name. | ||||
| If the new name is empty, it removes the anchor. | 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: | Position string formats: | ||||
| 123 = absolute pixels from parent origin | 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% = 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" | "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; | 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: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| String anchor1, anchor2; | String anchor1, anchor2; | ||||
| double value; | 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 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); | 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; | const String toString() const; | ||||
| // Tells the coord that an anchor is changing its name. | // 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; | Coordinate left, right, top, bottom; | ||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | |||||
| */ | |||||
| class ComponentAutoLayoutManager : public ComponentListener, | class ComponentAutoLayoutManager : public ComponentListener, | ||||
| public Coordinate::MarkerResolver, | |||||
| public Coordinate::NamedCoordinateFinder, | |||||
| public AsyncUpdater | public AsyncUpdater | ||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | |||||
| */ | |||||
| ComponentAutoLayoutManager (Component* parentComponent); | ComponentAutoLayoutManager (Component* parentComponent); | ||||
| /** Destructor. */ | |||||
| ~ComponentAutoLayoutManager(); | ~ComponentAutoLayoutManager(); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** | |||||
| */ | |||||
| void setMarker (const String& name, const Coordinate& coord); | 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(); | 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); | void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized); | ||||
| /** @internal */ | |||||
| void componentBeingDeleted (Component& component); | void componentBeingDeleted (Component& component); | ||||
| /** @internal */ | |||||
| void handleAsyncUpdate(); | void handleAsyncUpdate(); | ||||
| juce_UseDebuggingNewOperator | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| struct ComponentPosition | struct ComponentPosition | ||||
| @@ -213,6 +273,10 @@ private: | |||||
| Component* parent; | Component* parent; | ||||
| OwnedArray <ComponentPosition> components; | OwnedArray <ComponentPosition> components; | ||||
| OwnedArray <MarkerPosition> markers; | OwnedArray <MarkerPosition> markers; | ||||
| ComponentAutoLayoutManager (const ComponentAutoLayoutManager&); | |||||
| ComponentAutoLayoutManager& operator= (const ComponentAutoLayoutManager&); | |||||
| }; | }; | ||||
| #endif // __JUCER_COORDINATE_H_EF56ACFA__ | #endif // __JUCER_COORDINATE_H_EF56ACFA__ | ||||
| @@ -34,9 +34,9 @@ class CoordinatePropertyComponent : public PropertyComponent, | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| CoordinatePropertyComponent (Coordinate::MarkerResolver& resolver_, const String& name, | |||||
| CoordinatePropertyComponent (Coordinate::NamedCoordinateFinder& nameSource_, const String& name, | |||||
| const Value& coordValue_, bool isHorizontal_) | const Value& coordValue_, bool isHorizontal_) | ||||
| : PropertyComponent (name, 40), resolver (resolver_), | |||||
| : PropertyComponent (name, 40), nameSource (nameSource_), | |||||
| coordValue (coordValue_), | coordValue (coordValue_), | ||||
| textValue (Value (new CoordEditableValueSource (coordValue_, isHorizontal_))), | textValue (Value (new CoordEditableValueSource (coordValue_, isHorizontal_))), | ||||
| isHorizontal (isHorizontal_) | isHorizontal (isHorizontal_) | ||||
| @@ -98,26 +98,26 @@ public: | |||||
| if (button == proportionButton) | if (button == proportionButton) | ||||
| { | { | ||||
| coord.toggleProportionality (resolver); | |||||
| coord.toggleProportionality (nameSource, isHorizontal); | |||||
| coordValue = coord.toString(); | coordValue = coord.toString(); | ||||
| } | } | ||||
| else if (button == anchorButton1) | else if (button == anchorButton1) | ||||
| { | { | ||||
| const String marker (pickMarker (anchorButton1, coord.getAnchor1(), true)); | |||||
| const String marker (pickMarker (anchorButton1, coord.getAnchorName1(), true)); | |||||
| if (marker.isNotEmpty()) | if (marker.isNotEmpty()) | ||||
| { | { | ||||
| coord.changeAnchor1 (marker, resolver); | |||||
| coord.changeAnchor1 (marker, nameSource); | |||||
| coordValue = coord.toString(); | coordValue = coord.toString(); | ||||
| } | } | ||||
| } | } | ||||
| else if (button == anchorButton2) | else if (button == anchorButton2) | ||||
| { | { | ||||
| const String marker (pickMarker (anchorButton2, coord.getAnchor2(), false)); | |||||
| const String marker (pickMarker (anchorButton2, coord.getAnchorName2(), false)); | |||||
| if (marker.isNotEmpty()) | if (marker.isNotEmpty()) | ||||
| { | { | ||||
| coord.changeAnchor2 (marker, resolver); | |||||
| coord.changeAnchor2 (marker, nameSource); | |||||
| coordValue = coord.toString(); | coordValue = coord.toString(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -127,10 +127,10 @@ public: | |||||
| { | { | ||||
| Coordinate coord (getCoordinate()); | Coordinate coord (getCoordinate()); | ||||
| anchorButton1->setButtonText (coord.getAnchor1()); | |||||
| anchorButton1->setButtonText (coord.getAnchorName1()); | |||||
| anchorButton2->setVisible (coord.isProportional()); | anchorButton2->setVisible (coord.isProportional()); | ||||
| anchorButton2->setButtonText (coord.getAnchor2()); | |||||
| anchorButton2->setButtonText (coord.getAnchorName2()); | |||||
| resized(); | resized(); | ||||
| } | } | ||||
| @@ -142,7 +142,7 @@ public: | |||||
| virtual const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) = 0; | virtual const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) = 0; | ||||
| protected: | protected: | ||||
| Coordinate::MarkerResolver& resolver; | |||||
| Coordinate::NamedCoordinateFinder& nameSource; | |||||
| Value coordValue, textValue; | Value coordValue, textValue; | ||||
| Label* label; | Label* label; | ||||
| TextButton* proportionButton; | TextButton* proportionButton; | ||||
| @@ -168,15 +168,15 @@ protected: | |||||
| Coordinate coord (sourceValue.toString(), isHorizontal); | Coordinate coord (sourceValue.toString(), isHorizontal); | ||||
| if (coord.isProportional()) | if (coord.isProportional()) | ||||
| return String (coord.getEditableValue()) + "%"; | |||||
| return String (coord.getEditableNumber()) + "%"; | |||||
| return coord.getEditableValue(); | |||||
| return coord.getEditableNumber(); | |||||
| } | } | ||||
| void setValue (const var& newValue) | void setValue (const var& newValue) | ||||
| { | { | ||||
| Coordinate coord (sourceValue.toString(), isHorizontal); | Coordinate coord (sourceValue.toString(), isHorizontal); | ||||
| coord.setEditableValue ((double) newValue); | |||||
| coord.setEditableNumber ((double) newValue); | |||||
| const String newVal (coord.toString()); | const String newVal (coord.toString()); | ||||
| if (sourceValue != newVal) | if (sourceValue != newVal) | ||||
| @@ -30,7 +30,7 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| class MarkerListBase : public Coordinate::MarkerResolver | |||||
| class MarkerListBase : public Coordinate::NamedCoordinateFinder | |||||
| { | { | ||||
| public: | public: | ||||
| MarkerListBase (const ValueTree& group_, bool isX_) : group (group_), isX (isX_) {} | MarkerListBase (const ValueTree& group_, bool isX_) : group (group_), isX (isX_) {} | ||||
| @@ -133,10 +133,10 @@ public: | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| PositionPropertyComponent (MarkerResolver& resolver_, MarkerListBase& markerList_, | |||||
| PositionPropertyComponent (NamedCoordinateFinder& nameSource_, MarkerListBase& markerList_, | |||||
| const String& name, const ValueTree& markerState_, | const String& name, const ValueTree& markerState_, | ||||
| const Value& coordValue_) | const Value& coordValue_) | ||||
| : CoordinatePropertyComponent (resolver_, name, coordValue_, markerList_.isHorizontal()), | |||||
| : CoordinatePropertyComponent (nameSource_, name, coordValue_, markerList_.isHorizontal()), | |||||
| markerList (markerList_), | markerList (markerList_), | ||||
| markerState (markerState_) | markerState (markerState_) | ||||
| { | { | ||||
| @@ -80,3 +80,59 @@ private: | |||||
| Colour colour; | Colour colour; | ||||
| GlyphArrangement glyphs; | 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) | void drawSVG (Graphics& g) | ||||
| { | { | ||||
| if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 3000) | |||||
| if (Time::getCurrentTime().toMilliseconds() > lastSVGLoadTime.toMilliseconds() + 2000) | |||||
| { | { | ||||
| lastSVGLoadTime = Time::getCurrentTime(); | lastSVGLoadTime = Time::getCurrentTime(); | ||||
| createSVGDrawable(); | createSVGDrawable(); | ||||
| @@ -344,22 +344,23 @@ private: | |||||
| if (svgFileStream != 0) | if (svgFileStream != 0) | ||||
| { | { | ||||
| Drawable* loadedSVG = Drawable::createFromImageDataStream (*svgFileStream); | |||||
| svgDrawable = dynamic_cast <DrawableComposite*> (Drawable::createFromImageDataStream (*svgFileStream)); | |||||
| delete 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 | // 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. | // 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_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 3 | |||||
| #define JUCE_BUILDNUMBER 4 | |||||
| /** Current Juce version number. | /** 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... | 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))) \ | #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 | #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier | ||||
| #if JUCE_PPC || JUCE_IPHONE | #if JUCE_PPC || JUCE_IPHONE | ||||
| @@ -12955,7 +12955,7 @@ public: | |||||
| The type is specified when the ValueTree is created. | The type is specified when the ValueTree is created. | ||||
| @see hasType | @see hasType | ||||
| */ | */ | ||||
| const String getType() const; | |||||
| const Identifier getType() const; | |||||
| /** Returns true if the node has this type. | /** Returns true if the node has this type. | ||||
| The comparison is case-sensitive. | The comparison is case-sensitive. | ||||
| @@ -18856,6 +18856,16 @@ public: | |||||
| */ | */ | ||||
| const AffineTransform inverted() const throw(); | 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. */ | /** Returns the result of concatenating another transformation after this one. */ | ||||
| const AffineTransform followedBy (const AffineTransform& other) const throw(); | 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. */ | /** 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); } | 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". */ | /** Returns the point as a string in the form "x, y". */ | ||||
| const String toString() const { return String (x) + ", " + String (y); } | const String toString() const { return String (x) + ", " + String (y); } | ||||
| @@ -20451,11 +20464,16 @@ public: | |||||
| return false; | 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() | 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 newX = jmin (x, other.x); | ||||
| const ValueType newY = jmin (y, other.y); | const ValueType newY = jmin (y, other.y); | ||||
| @@ -20839,6 +20857,9 @@ public: | |||||
| /** Copies this path from another one. */ | /** Copies this path from another one. */ | ||||
| Path& operator= (const Path& other); | 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. */ | /** Returns true if the path doesn't contain any lines or curves. */ | ||||
| bool isEmpty() const throw(); | 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 | The internal data of the two paths is swapped over, so this is much faster than | ||||
| copying it to a temp variable and back. | 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. | /** Applies a 2D transform to all the vertices in the path. | ||||
| @@ -23140,6 +23161,9 @@ public: | |||||
| */ | */ | ||||
| bool isRadial; | bool isRadial; | ||||
| bool operator== (const ColourGradient& other) const throw(); | |||||
| bool operator!= (const ColourGradient& other) const throw(); | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| @@ -23151,6 +23175,9 @@ private: | |||||
| : position (position_), colour (colour_) | : 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; | uint32 position; | ||||
| Colour colour; | Colour colour; | ||||
| }; | }; | ||||
| @@ -41889,24 +41916,65 @@ public: | |||||
| */ | */ | ||||
| static Drawable* createFromSVG (const XmlElement& svgDocument); | 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. | /** Tries to create a Drawable from a previously-saved ValueTree. | ||||
| The ValueTree must have been created by the createValueTree() method. | 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. | /** Creates a ValueTree to represent this Drawable. | ||||
| The VarTree that is returned can be turned back into a Drawable with | The VarTree that is returned can be turned back into a Drawable with | ||||
| createFromValueTree(). | 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 | juce_UseDebuggingNewOperator | ||||
| protected: | |||||
| static const Identifier idProperty; | |||||
| private: | private: | ||||
| String name; | |||||
| Drawable (const Drawable&); | Drawable (const Drawable&); | ||||
| Drawable& operator= (const Drawable&); | Drawable& operator= (const Drawable&); | ||||
| String name; | |||||
| }; | }; | ||||
| #endif // __JUCE_DRAWABLE_JUCEHEADER__ | #endif // __JUCE_DRAWABLE_JUCEHEADER__ | ||||
| @@ -50657,6 +50725,7 @@ private: | |||||
| Component* panelComponent; | Component* panelComponent; | ||||
| int tabDepth; | int tabDepth; | ||||
| int outlineThickness, edgeIndent; | int outlineThickness, edgeIndent; | ||||
| static const Identifier deleteComponentId; | |||||
| friend class TabCompButtonBar; | friend class TabCompButtonBar; | ||||
| void changeCallback (int newCurrentTabIndex, const String& newTabName); | void changeCallback (int newCurrentTabIndex, const String& newTabName); | ||||
| @@ -57436,16 +57505,12 @@ public: | |||||
| @param drawable the object to add - this will be deleted automatically | @param drawable the object to add - this will be deleted automatically | ||||
| when no longer needed, so the caller mustn't keep any | when no longer needed, so the caller mustn't keep any | ||||
| pointers to it. | 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, | @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() | -1 is the front, or any value from 0 and getNumDrawables() | ||||
| can be used | can be used | ||||
| @see removeDrawable | @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. | /** Adds a new sub-drawable to this one. | ||||
| @@ -57454,16 +57519,12 @@ public: | |||||
| pointer instead. | pointer instead. | ||||
| @param drawable the object to add - an internal copy will be made of this object | @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, | @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() | -1 is the front, or any value from 0 and getNumDrawables() | ||||
| can be used | can be used | ||||
| @see removeDrawable | @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. | /** Deletes one of the Drawable objects. | ||||
| @@ -57493,15 +57554,6 @@ public: | |||||
| */ | */ | ||||
| Drawable* getDrawable (int index) const throw() { return drawables [index]; } | 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. | /** Brings one of the Drawables to the front. | ||||
| @param index the index of the drawable to move, between 0 | @param index the index of the drawable to move, between 0 | ||||
| @@ -57510,6 +57562,38 @@ public: | |||||
| */ | */ | ||||
| void bringToFront (int index); | 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 */ | /** @internal */ | ||||
| void render (const Drawable::RenderingContext& context) const; | void render (const Drawable::RenderingContext& context) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -57517,17 +57601,28 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| bool hitTest (float x, float y) const; | bool hitTest (float x, float y) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| int getNumControlPoints() const; | |||||
| /** @internal */ | |||||
| const Point<float> getControlPoint (int index) const; | |||||
| /** @internal */ | |||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @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 | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| OwnedArray <Drawable> drawables; | OwnedArray <Drawable> drawables; | ||||
| OwnedArray <AffineTransform> transforms; | |||||
| Point<float> controlPoints[3]; | |||||
| const Rectangle<float> getUntransformedBounds() const; | |||||
| const AffineTransform getTransform() const; | |||||
| DrawableComposite (const DrawableComposite&); | DrawableComposite (const DrawableComposite&); | ||||
| DrawableComposite& operator= (const DrawableComposite&); | DrawableComposite& operator= (const DrawableComposite&); | ||||
| @@ -57568,9 +57663,6 @@ public: | |||||
| /** Sets the image that this drawable will render. | /** 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 | 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 | with ImageCache and pass it in here with releaseWhenNotNeeded = true, then | ||||
| it'll be released neatly with its reference count being decreased. | 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, | needs it - unless the image was created by the ImageCache, | ||||
| in which case it will be released with ImageCache::release(). | 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. */ | /** Returns the current image. */ | ||||
| Image* getImage() const throw() { return image; } | Image* getImage() const throw() { return image; } | ||||
| @@ -57610,6 +57701,38 @@ public: | |||||
| /** Returns the overlay colour. */ | /** Returns the overlay colour. */ | ||||
| const Colour& getOverlayColour() const throw() { return overlayColour; } | 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 */ | /** @internal */ | ||||
| void render (const Drawable::RenderingContext& context) const; | void render (const Drawable::RenderingContext& context) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| @@ -57619,9 +57742,13 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | |||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||||
| /** @internal */ | /** @internal */ | ||||
| static DrawableImage* createFromValueTree (const ValueTree& tree); | |||||
| static const Identifier valueTreeType; | |||||
| /** @internal */ | |||||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -57630,6 +57757,9 @@ private: | |||||
| bool canDeleteImage; | bool canDeleteImage; | ||||
| float opacity; | float opacity; | ||||
| Colour overlayColour; | Colour overlayColour; | ||||
| Point<float> controlPoints[3]; | |||||
| const AffineTransform getTransform() const; | |||||
| DrawableImage (const DrawableImage&); | DrawableImage (const DrawableImage&); | ||||
| DrawableImage& operator= (const DrawableImage&); | DrawableImage& operator= (const DrawableImage&); | ||||
| @@ -57719,9 +57849,13 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @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 | juce_UseDebuggingNewOperator | ||||
| @@ -57791,9 +57925,13 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @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 */ | /** @internal */ | ||||
| static DrawableText* createFromValueTree (const ValueTree& tree); | |||||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -88,7 +88,7 @@ public: | |||||
| { | { | ||||
| bufferList.calloc (256, 1); | bufferList.calloc (256, 1); | ||||
| #ifdef WIN32 | |||||
| #if JUCE_WINDOWS | |||||
| if (InitializeQTML (0) != noErr) | if (InitializeQTML (0) != noErr) | ||||
| return; | return; | ||||
| #endif | #endif | ||||
| @@ -566,9 +566,9 @@ bool ValueTree::hasType (const Identifier& typeName) const | |||||
| return object != 0 && object->type == typeName; | 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 | ValueTree ValueTree::getParent() const | ||||
| @@ -807,7 +807,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) | |||||
| //============================================================================== | //============================================================================== | ||||
| void ValueTree::writeToStream (OutputStream& output) | void ValueTree::writeToStream (OutputStream& output) | ||||
| { | { | ||||
| output.writeString (getType()); | |||||
| output.writeString (getType().toString()); | |||||
| const int numProps = getNumProperties(); | const int numProps = getNumProperties(); | ||||
| output.writeCompressedInt (numProps); | output.writeCompressedInt (numProps); | ||||
| @@ -125,7 +125,7 @@ public: | |||||
| The type is specified when the ValueTree is created. | The type is specified when the ValueTree is created. | ||||
| @see hasType | @see hasType | ||||
| */ | */ | ||||
| const String getType() const; | |||||
| const Identifier getType() const; | |||||
| /** Returns true if the node has this type. | /** Returns true if the node has this type. | ||||
| The comparison is case-sensitive. | 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... | 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))) \ | #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 | #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier | ||||
| #if JUCE_PPC || JUCE_IPHONE | #if JUCE_PPC || JUCE_IPHONE | ||||
| @@ -33,7 +33,7 @@ | |||||
| */ | */ | ||||
| #define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
| #define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
| #define JUCE_BUILDNUMBER 3 | |||||
| #define JUCE_BUILDNUMBER 4 | |||||
| /** Current Juce version number. | /** 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 Point<int> pos (rowComp->relativePositionToOtherComponent (this, Point<int>())); | ||||
| const Rectangle<int> rowRect (pos.getX(), pos.getY(), rowComp->getWidth(), rowComp->getHeight()); | 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; | return flags.isFocusContainerFlag; | ||||
| } | } | ||||
| static const Identifier juce_explicitFocusOrderId ("_jexfo"); | |||||
| int Component::getExplicitFocusOrder() const | int Component::getExplicitFocusOrder() const | ||||
| { | { | ||||
| return properties ["_jexfo"]; | |||||
| return properties [juce_explicitFocusOrderId]; | |||||
| } | } | ||||
| void Component::setExplicitFocusOrder (const int newFocusOrderIndex) | void Component::setExplicitFocusOrder (const int newFocusOrderIndex) | ||||
| { | { | ||||
| properties.set ("_jexfo", newFocusOrderIndex); | |||||
| properties.set (juce_explicitFocusOrderId, newFocusOrderIndex); | |||||
| } | } | ||||
| KeyboardFocusTraverser* Component::createFocusTraverser() | KeyboardFocusTraverser* Component::createFocusTraverser() | ||||
| @@ -118,6 +118,8 @@ TabBarButton* TabbedComponent::createTabButton (const String& tabName, const int | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| const Identifier TabbedComponent::deleteComponentId ("deleteByTabComp_"); | |||||
| void TabbedComponent::clearTabs() | void TabbedComponent::clearTabs() | ||||
| { | { | ||||
| if (panelComponent != 0) | 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 | // be careful not to delete these components until they've been removed from the tab component | ||||
| jassert (c == 0 || c->isValidComponent()); | jassert (c == 0 || c->isValidComponent()); | ||||
| if (c != 0 && (bool) c->getProperties() ["deleteByTabComp_"]) | |||||
| if (c != 0 && (bool) c->getProperties() [deleteComponentId]) | |||||
| delete c; | delete c; | ||||
| } | } | ||||
| @@ -152,7 +154,7 @@ void TabbedComponent::addTab (const String& tabName, | |||||
| contentComponents.insert (insertIndex, contentComponent); | contentComponents.insert (insertIndex, contentComponent); | ||||
| if (contentComponent != 0) | if (contentComponent != 0) | ||||
| contentComponent->getProperties().set ("deleteByTabComp_", deleteComponentWhenNotNeeded); | |||||
| contentComponent->getProperties().set (deleteComponentId, deleteComponentWhenNotNeeded); | |||||
| tabs->addTab (tabName, tabBackgroundColour, insertIndex); | tabs->addTab (tabName, tabBackgroundColour, insertIndex); | ||||
| } | } | ||||
| @@ -166,7 +168,7 @@ void TabbedComponent::removeTab (const int tabIndex) | |||||
| { | { | ||||
| Component* const c = contentComponents [tabIndex]; | Component* const c = contentComponents [tabIndex]; | ||||
| if (c != 0 && (bool) c->getProperties() ["deleteByTabComp_"]) | |||||
| if (c != 0 && (bool) c->getProperties() [deleteComponentId]) | |||||
| { | { | ||||
| if (c == panelComponent) | if (c == panelComponent) | ||||
| panelComponent = 0; | panelComponent = 0; | ||||
| @@ -232,6 +232,7 @@ private: | |||||
| Component* panelComponent; | Component* panelComponent; | ||||
| int tabDepth; | int tabDepth; | ||||
| int outlineThickness, edgeIndent; | int outlineThickness, edgeIndent; | ||||
| static const Identifier deleteComponentId; | |||||
| friend class TabCompButtonBar; | friend class TabCompButtonBar; | ||||
| void changeCallback (int newCurrentTabIndex, const String& newTabName); | 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))) | 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.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; | ++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() | void ColourGradient::clearColours() | ||||
| { | { | ||||
| @@ -141,6 +141,9 @@ public: | |||||
| */ | */ | ||||
| bool isRadial; | bool isRadial; | ||||
| bool operator== (const ColourGradient& other) const throw(); | |||||
| bool operator!= (const ColourGradient& other) const throw(); | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -153,6 +156,9 @@ private: | |||||
| : position (position_), colour (colour_) | : 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; | uint32 position; | ||||
| Colour colour; | Colour colour; | ||||
| }; | }; | ||||
| @@ -36,6 +36,8 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "../../../text/juce_XmlDocument.h" | #include "../../../text/juce_XmlDocument.h" | ||||
| #include "../../../io/files/juce_FileInputStream.h" | #include "../../../io/files/juce_FileInputStream.h" | ||||
| const Identifier Drawable::idProperty ("id"); | |||||
| //============================================================================== | //============================================================================== | ||||
| Drawable::RenderingContext::RenderingContext (Graphics& g_, | Drawable::RenderingContext::RenderingContext (Graphics& g_, | ||||
| const AffineTransform& transform_, | 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; | return d; | ||||
| } | } | ||||
| @@ -180,25 +180,66 @@ public: | |||||
| static Drawable* createFromSVG (const XmlElement& svgDocument); | 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. | /** Tries to create a Drawable from a previously-saved ValueTree. | ||||
| The ValueTree must have been created by the createValueTree() method. | 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. | /** Creates a ValueTree to represent this Drawable. | ||||
| The VarTree that is returned can be turned back into a Drawable with | The VarTree that is returned can be turned back into a Drawable with | ||||
| createFromValueTree(). | 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 | juce_UseDebuggingNewOperator | ||||
| protected: | |||||
| static const Identifier idProperty; | |||||
| private: | private: | ||||
| String name; | |||||
| Drawable (const Drawable&); | Drawable (const Drawable&); | ||||
| Drawable& operator= (const Drawable&); | Drawable& operator= (const Drawable&); | ||||
| String name; | |||||
| }; | }; | ||||
| @@ -37,6 +37,8 @@ BEGIN_JUCE_NAMESPACE | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableComposite::DrawableComposite() | DrawableComposite::DrawableComposite() | ||||
| { | { | ||||
| controlPoints[1].setXY (1.0f, 0.0f); | |||||
| controlPoints[2].setXY (0.0f, 1.0f); | |||||
| } | } | ||||
| DrawableComposite::~DrawableComposite() | 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 (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) | void DrawableComposite::removeDrawable (const int index, const bool deleteDrawable) | ||||
| { | { | ||||
| drawables.remove (index, deleteDrawable); | drawables.remove (index, deleteDrawable); | ||||
| transforms.remove (index); | |||||
| } | } | ||||
| void DrawableComposite::bringToFront (const int index) | void DrawableComposite::bringToFront (const int index) | ||||
| { | { | ||||
| if (index >= 0 && index < drawables.size() - 1) | if (index >= 0 && index < drawables.size() - 1) | ||||
| { | |||||
| drawables.move (index, -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 | void DrawableComposite::render (const Drawable::RenderingContext& context) const | ||||
| { | { | ||||
| if (drawables.size() > 0 && context.opacity > 0) | 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) | if (context.opacity >= 1.0f || drawables.size() == 1) | ||||
| { | { | ||||
| Drawable::RenderingContext contextCopy (context); | Drawable::RenderingContext contextCopy (context); | ||||
| contextCopy.transform = getTransform().followedBy (context.transform); | |||||
| for (int i = 0; i < drawables.size(); ++i) | 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); | drawables.getUnchecked(i)->render (contextCopy); | ||||
| } | |||||
| } | } | ||||
| else | 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; | Rectangle<float> bounds; | ||||
| for (int i = 0; i < drawables.size(); ++i) | 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; | 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 true; | ||||
| } | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -171,87 +151,123 @@ Drawable* DrawableComposite::createCopy() const | |||||
| { | { | ||||
| DrawableComposite* const dc = new DrawableComposite(); | 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) | for (int i = 0; i < drawables.size(); ++i) | ||||
| { | |||||
| dc->drawables.add (drawables.getUnchecked(i)->createCopy()); | 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; | 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) | 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) | 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 | @param drawable the object to add - this will be deleted automatically | ||||
| when no longer needed, so the caller mustn't keep any | when no longer needed, so the caller mustn't keep any | ||||
| pointers to it. | 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, | @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() | -1 is the front, or any value from 0 and getNumDrawables() | ||||
| can be used | can be used | ||||
| @see removeDrawable | @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. | /** Adds a new sub-drawable to this one. | ||||
| @@ -73,16 +69,12 @@ public: | |||||
| pointer instead. | pointer instead. | ||||
| @param drawable the object to add - an internal copy will be made of this object | @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, | @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() | -1 is the front, or any value from 0 and getNumDrawables() | ||||
| can be used | can be used | ||||
| @see removeDrawable | @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. | /** Deletes one of the Drawable objects. | ||||
| @@ -112,15 +104,6 @@ public: | |||||
| */ | */ | ||||
| Drawable* getDrawable (int index) const throw() { return drawables [index]; } | 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. | /** Brings one of the Drawables to the front. | ||||
| @param index the index of the drawable to move, between 0 | @param index the index of the drawable to move, between 0 | ||||
| @@ -129,6 +112,37 @@ public: | |||||
| */ | */ | ||||
| void bringToFront (int index); | 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 */ | /** @internal */ | ||||
| @@ -138,18 +152,29 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| bool hitTest (float x, float y) const; | bool hitTest (float x, float y) const; | ||||
| /** @internal */ | /** @internal */ | ||||
| int getNumControlPoints() const; | |||||
| /** @internal */ | |||||
| const Point<float> getControlPoint (int index) const; | |||||
| /** @internal */ | |||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | |||||
| const ValueTree createValueTree (ImageProvider* imageProvider) const; | |||||
| /** @internal */ | /** @internal */ | ||||
| static DrawableComposite* createFromValueTree (const ValueTree& tree); | |||||
| static const Identifier valueTreeType; | |||||
| /** @internal */ | |||||
| const Identifier getValueTreeType() const { return valueTreeType; } | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| private: | private: | ||||
| OwnedArray <Drawable> drawables; | OwnedArray <Drawable> drawables; | ||||
| OwnedArray <AffineTransform> transforms; | |||||
| Point<float> controlPoints[3]; | |||||
| const Rectangle<float> getUntransformedBounds() const; | |||||
| const AffineTransform getTransform() const; | |||||
| DrawableComposite (const DrawableComposite&); | DrawableComposite (const DrawableComposite&); | ||||
| DrawableComposite& operator= (const DrawableComposite&); | DrawableComposite& operator= (const DrawableComposite&); | ||||
| @@ -30,8 +30,7 @@ BEGIN_JUCE_NAMESPACE | |||||
| #include "juce_DrawableImage.h" | #include "juce_DrawableImage.h" | ||||
| #include "../imaging/juce_ImageCache.h" | #include "../imaging/juce_ImageCache.h" | ||||
| #include "../imaging/juce_ImageFileFormat.h" | |||||
| #include "../../../io/streams/juce_MemoryOutputStream.h" | |||||
| //============================================================================== | //============================================================================== | ||||
| DrawableImage::DrawableImage() | DrawableImage::DrawableImage() | ||||
| @@ -40,6 +39,8 @@ DrawableImage::DrawableImage() | |||||
| opacity (1.0f), | opacity (1.0f), | ||||
| overlayColour (0x00000000) | overlayColour (0x00000000) | ||||
| { | { | ||||
| controlPoints[1].setXY (1.0f, 0.0f); | |||||
| controlPoints[2].setXY (0.0f, 1.0f); | |||||
| } | } | ||||
| DrawableImage::~DrawableImage() | DrawableImage::~DrawableImage() | ||||
| @@ -61,6 +62,10 @@ void DrawableImage::setImage (const Image& imageToCopy) | |||||
| clearImage(); | clearImage(); | ||||
| image = new Image (imageToCopy); | image = new Image (imageToCopy); | ||||
| canDeleteImage = true; | 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, | void DrawableImage::setImage (Image* imageToUse, | ||||
| @@ -69,6 +74,13 @@ void DrawableImage::setImage (Image* imageToUse, | |||||
| clearImage(); | clearImage(); | ||||
| image = imageToUse; | image = imageToUse; | ||||
| canDeleteImage = releaseWhenNotNeeded; | 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) | void DrawableImage::setOpacity (const float newOpacity) | ||||
| @@ -81,23 +93,45 @@ void DrawableImage::setOverlayColour (const Colour& newOverlayColour) | |||||
| overlayColour = 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 | void DrawableImage::render (const Drawable::RenderingContext& context) const | ||||
| { | { | ||||
| if (image != 0) | if (image != 0) | ||||
| { | { | ||||
| const AffineTransform t (getTransform().followedBy (context.transform)); | |||||
| if (opacity > 0.0f && ! overlayColour.isOpaque()) | if (opacity > 0.0f && ! overlayColour.isOpaque()) | ||||
| { | { | ||||
| context.g.setOpacity (context.opacity * opacity); | 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()) | if (! overlayColour.isTransparent()) | ||||
| { | { | ||||
| context.g.setColour (overlayColour.withMultipliedAlpha (context.opacity)); | 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) | if (image == 0) | ||||
| return Rectangle<float>(); | 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 | 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 | Drawable* DrawableImage::createCopy() const | ||||
| @@ -127,6 +182,9 @@ Drawable* DrawableImage::createCopy() const | |||||
| di->opacity = opacity; | di->opacity = opacity; | ||||
| di->overlayColour = overlayColour; | di->overlayColour = overlayColour; | ||||
| for (int i = 0; i < 4; ++i) | |||||
| di->controlPoints[i] = controlPoints[i]; | |||||
| if (image != 0) | if (image != 0) | ||||
| { | { | ||||
| if ((! canDeleteImage) || ! ImageCache::isImageInCache (image)) | 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. | /** 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 | 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 | with ImageCache and pass it in here with releaseWhenNotNeeded = true, then | ||||
| it'll be released neatly with its reference count being decreased. | 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, | needs it - unless the image was created by the ImageCache, | ||||
| in which case it will be released with ImageCache::release(). | 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. */ | /** Returns the current image. */ | ||||
| Image* getImage() const throw() { return image; } | Image* getImage() const throw() { return image; } | ||||
| @@ -97,6 +93,37 @@ public: | |||||
| /** Returns the overlay colour. */ | /** Returns the overlay colour. */ | ||||
| const Colour& getOverlayColour() const throw() { return overlayColour; } | 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 */ | /** @internal */ | ||||
| @@ -108,9 +135,13 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @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 | juce_UseDebuggingNewOperator | ||||
| @@ -120,6 +151,9 @@ private: | |||||
| bool canDeleteImage; | bool canDeleteImage; | ||||
| float opacity; | float opacity; | ||||
| Colour overlayColour; | Colour overlayColour; | ||||
| Point<float> controlPoints[3]; | |||||
| const AffineTransform getTransform() const; | |||||
| DrawableImage (const DrawableImage&); | DrawableImage (const DrawableImage&); | ||||
| DrawableImage& operator= (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 */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @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 | 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! | 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! | jassertfalse; // xxx not finished! | ||||
| return dt; | |||||
| return v; | |||||
| } | } | ||||
| @@ -78,9 +78,13 @@ public: | |||||
| /** @internal */ | /** @internal */ | ||||
| Drawable* createCopy() const; | Drawable* createCopy() const; | ||||
| /** @internal */ | /** @internal */ | ||||
| ValueTree createValueTree() const; | |||||
| const Rectangle<float> refreshFromValueTree (const ValueTree& tree, ImageProvider* imageProvider); | |||||
| /** @internal */ | /** @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 | juce_UseDebuggingNewOperator | ||||
| @@ -615,24 +615,13 @@ const Rectangle<float> GlyphArrangement::getBoundingBox (int startIndex, int num | |||||
| num = glyphs.size() - startIndex; | num = glyphs.size() - startIndex; | ||||
| Rectangle<float> result; | Rectangle<float> result; | ||||
| bool isFirst = true; | |||||
| while (--num >= 0) | while (--num >= 0) | ||||
| { | { | ||||
| const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); | const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); | ||||
| if (includeWhitespace || ! pg->isWhitespace()) | if (includeWhitespace || ! pg->isWhitespace()) | ||||
| { | |||||
| if (isFirst) | |||||
| { | |||||
| isFirst = false; | |||||
| result = pg->getBounds(); | |||||
| } | |||||
| else | |||||
| { | |||||
| result = result.getUnion (pg->getBounds()); | |||||
| } | |||||
| } | |||||
| result = result.getUnion (pg->getBounds()); | |||||
| } | } | ||||
| return result; | return result; | ||||
| @@ -233,6 +233,14 @@ bool AffineTransform::isSingularity() const throw() | |||||
| return (mat00 * mat11 - mat10 * mat01) == 0.0; | 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() | bool AffineTransform::isOnlyTranslation() const throw() | ||||
| { | { | ||||
| return (mat01 == 0) | return (mat01 == 0) | ||||
| @@ -146,6 +146,16 @@ public: | |||||
| */ | */ | ||||
| const AffineTransform inverted() const throw(); | 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. */ | /** Returns the result of concatenating another transformation after this one. */ | ||||
| const AffineTransform followedBy (const AffineTransform& other) const throw(); | const AffineTransform followedBy (const AffineTransform& other) const throw(); | ||||
| @@ -141,6 +141,23 @@ Path& Path::operator= (const Path& other) | |||||
| return *this; | 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() | void Path::clear() throw() | ||||
| { | { | ||||
| numElements = 0; | numElements = 0; | ||||
| @@ -150,7 +167,7 @@ void Path::clear() throw() | |||||
| pathXMax = 0; | pathXMax = 0; | ||||
| } | } | ||||
| void Path::swapWithPath (Path& other) | |||||
| void Path::swapWithPath (Path& other) throw() | |||||
| { | { | ||||
| data.swapWith (other.data); | data.swapWith (other.data); | ||||
| swapVariables <size_t> (numElements, other.numElements); | swapVariables <size_t> (numElements, other.numElements); | ||||
| @@ -204,7 +221,6 @@ const Rectangle<float> Path::getBounds() const throw() | |||||
| pathYMax - pathYMin); | pathYMax - pathYMin); | ||||
| } | } | ||||
| const Rectangle<float> Path::getBoundsTransformed (const AffineTransform& transform) const throw() | const Rectangle<float> Path::getBoundsTransformed (const AffineTransform& transform) const throw() | ||||
| { | { | ||||
| return getBounds().transformed (transform); | return getBounds().transformed (transform); | ||||
| @@ -84,6 +84,9 @@ public: | |||||
| /** Copies this path from another one. */ | /** Copies this path from another one. */ | ||||
| Path& operator= (const Path& other); | 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. */ | /** Returns true if the path doesn't contain any lines or curves. */ | ||||
| bool isEmpty() const throw(); | 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 | The internal data of the two paths is swapped over, so this is much faster than | ||||
| copying it to a temp variable and back. | 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. | /** 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. */ | /** 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); } | 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". */ | /** Returns the point as a string in the form "x, y". */ | ||||
| const String toString() const { return String (x) + ", " + String (y); } | const String toString() const { return String (x) + ", " + String (y); } | ||||
| @@ -395,11 +395,16 @@ public: | |||||
| return false; | 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() | 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 newX = jmin (x, other.x); | ||||
| const ValueType newY = jmin (y, other.y); | 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; | static InternalMessageQueue* juce_internalMessageQueue = 0; | ||||
| @@ -327,6 +315,17 @@ void MessageManager::broadcastMessage (const String& value) throw() | |||||
| /* TODO */ | /* TODO */ | ||||
| } | } | ||||
| struct MessageThreadFuncCall | |||||
| { | |||||
| enum { uniqueID = 0x73774623 }; | |||||
| MessageCallbackFunction* func; | |||||
| void* parameter; | |||||
| void* result; | |||||
| CriticalSection lock; | |||||
| WaitableEvent event; | |||||
| }; | |||||
| void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, | void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, | ||||
| void* parameter) | void* parameter) | ||||
| { | { | ||||
| @@ -36,81 +36,24 @@ struct CallbackMessagePayload | |||||
| }; | }; | ||||
| END_JUCE_NAMESPACE | 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; | - (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 | - (void) performCallback: (id) info | ||||
| { | { | ||||
| if ([info isKindOfClass: [NSData class]]) | if ([info isKindOfClass: [NSData class]]) | ||||
| { | { | ||||
| CallbackMessagePayload* pl = (CallbackMessagePayload*) [((NSData*) info) bytes]; | |||||
| JUCE_NAMESPACE::CallbackMessagePayload* pl = (JUCE_NAMESPACE::CallbackMessagePayload*) [((NSData*) info) bytes]; | |||||
| if (pl != 0) | if (pl != 0) | ||||
| { | { | ||||
| @@ -128,8 +71,7 @@ using namespace JUCE_NAMESPACE; | |||||
| BEGIN_JUCE_NAMESPACE | BEGIN_JUCE_NAMESPACE | ||||
| static JuceAppDelegate* juceAppDelegate = 0; | |||||
| //============================================================================== | |||||
| void MessageManager::runDispatchLoop() | void MessageManager::runDispatchLoop() | ||||
| { | { | ||||
| jassert (isThisTheMessageThread()); // must only be called by the message thread | jassert (isThisTheMessageThread()); // must only be called by the message thread | ||||
| @@ -167,6 +109,7 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) | |||||
| static CFRunLoopRef runLoop = 0; | static CFRunLoopRef runLoop = 0; | ||||
| static CFRunLoopSourceRef runLoopSource = 0; | static CFRunLoopSourceRef runLoopSource = 0; | ||||
| static Array <void*, CriticalSection>* pendingMessages = 0; | static Array <void*, CriticalSection>* pendingMessages = 0; | ||||
| static JuceCustomMessageHandler* juceCustomMessageHandler = 0; | |||||
| static void runLoopSourceCallback (void*) | static void runLoopSourceCallback (void*) | ||||
| { | { | ||||
| @@ -202,8 +145,8 @@ void MessageManager::doPlatformSpecificInitialisation() | |||||
| runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); | runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); | ||||
| CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); | CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); | ||||
| if (juceAppDelegate == 0) | |||||
| juceAppDelegate = [[JuceAppDelegate alloc] init]; | |||||
| if (juceCustomMessageHandler == 0) | |||||
| juceCustomMessageHandler = [[JuceCustomMessageHandler alloc] init]; | |||||
| } | } | ||||
| void MessageManager::doPlatformSpecificShutdown() | void MessageManager::doPlatformSpecificShutdown() | ||||
| @@ -220,11 +163,11 @@ void MessageManager::doPlatformSpecificShutdown() | |||||
| deleteAndZero (pendingMessages); | 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.result = 0; | ||||
| cmp.hasBeenExecuted = false; | 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; | return cmp.result; | ||||
| } | } | ||||