| @@ -36,24 +36,24 @@ public: | |||||
| Component* createComponent() { return new TextButton (String::empty); } | Component* createComponent() { return new TextButton (String::empty); } | ||||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 150, 24); } | const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 150, 24); } | ||||
| void updateComponent (Component* comp, const ValueTree& state) | |||||
| void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) | |||||
| { | { | ||||
| TextButton* tb = dynamic_cast <TextButton*> (comp); | TextButton* tb = dynamic_cast <TextButton*> (comp); | ||||
| jassert (tb != 0); | jassert (tb != 0); | ||||
| ComponentTypeHandler::updateComponent (comp, state); | |||||
| ComponentTypeHandler::updateComponent (document, comp, state); | |||||
| tb->setButtonText (state ["text"].toString()); | tb->setButtonText (state ["text"].toString()); | ||||
| } | } | ||||
| void initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||||
| void initialiseNewItem (ComponentDocument& document, ValueTree& state) | |||||
| { | { | ||||
| ComponentTypeHandler::initialiseNewItem (state, document); | |||||
| ComponentTypeHandler::initialiseNewItem (document, state); | |||||
| state.setProperty ("text", "New Toggle Button", 0); | state.setProperty ("text", "New Toggle Button", 0); | ||||
| } | } | ||||
| void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props) | |||||
| void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||||
| { | { | ||||
| ComponentTypeHandler::createPropertyEditors (state, document, props); | |||||
| ComponentTypeHandler::createPropertyEditors (document, state, props); | |||||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | ||||
| props.getLast()->setTooltip ("The button's text."); | props.getLast()->setTooltip ("The button's text."); | ||||
| } | } | ||||
| @@ -36,24 +36,24 @@ public: | |||||
| Component* createComponent() { return new ToggleButton (String::empty); } | Component* createComponent() { return new ToggleButton (String::empty); } | ||||
| const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 180, 24); } | const Rectangle<int> getDefaultSize() { return Rectangle<int> (0, 0, 180, 24); } | ||||
| void updateComponent (Component* comp, const ValueTree& state) | |||||
| void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) | |||||
| { | { | ||||
| ToggleButton* tb = dynamic_cast <ToggleButton*> (comp); | ToggleButton* tb = dynamic_cast <ToggleButton*> (comp); | ||||
| jassert (tb != 0); | jassert (tb != 0); | ||||
| ComponentTypeHandler::updateComponent (comp, state); | |||||
| ComponentTypeHandler::updateComponent (document, comp, state); | |||||
| tb->setButtonText (state ["text"].toString()); | tb->setButtonText (state ["text"].toString()); | ||||
| } | } | ||||
| void initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||||
| void initialiseNewItem (ComponentDocument& document, ValueTree& state) | |||||
| { | { | ||||
| ComponentTypeHandler::initialiseNewItem (state, document); | |||||
| ComponentTypeHandler::initialiseNewItem (document, state); | |||||
| state.setProperty ("text", "New Toggle Button", 0); | state.setProperty ("text", "New Toggle Button", 0); | ||||
| } | } | ||||
| void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props) | |||||
| void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||||
| { | { | ||||
| ComponentTypeHandler::createPropertyEditors (state, document, props); | |||||
| ComponentTypeHandler::createPropertyEditors (document, state, props); | |||||
| props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | props.add (new TextPropertyComponent (getValue ("text", state, document), "Button Text", 1024, false)); | ||||
| props.getLast()->setTooltip ("The button's text."); | props.getLast()->setTooltip ("The button's text."); | ||||
| } | } | ||||
| @@ -57,28 +57,30 @@ Value ComponentTypeHandler::getValue (const var::identifier& name, ValueTree& st | |||||
| return state.getPropertyAsValue (name, document.getUndoManager()); | return state.getPropertyAsValue (name, document.getUndoManager()); | ||||
| } | } | ||||
| void ComponentTypeHandler::updateComponent (Component* comp, const ValueTree& state) | |||||
| void ComponentTypeHandler::updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state) | |||||
| { | { | ||||
| if (comp->getParentComponent() != 0) | if (comp->getParentComponent() != 0) | ||||
| { | { | ||||
| PositionedRectangle pos (state [compBoundsProperty].toString()); | |||||
| comp->setBounds (pos.getRectangle (comp->getParentComponent()->getLocalBounds())); | |||||
| RectangleCoordinates pos (state [compBoundsProperty].toString()); | |||||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (state, comp->getParentComponent())); | |||||
| comp->setBounds (pos.resolve (*markers)); | |||||
| } | } | ||||
| comp->setName (state [compNameProperty]); | comp->setName (state [compNameProperty]); | ||||
| } | } | ||||
| void ComponentTypeHandler::initialiseNewItem (ValueTree& state, ComponentDocument& document) | |||||
| void ComponentTypeHandler::initialiseNewItem (ComponentDocument& document, ValueTree& state) | |||||
| { | { | ||||
| state.setProperty (compNameProperty, String::empty, 0); | state.setProperty (compNameProperty, String::empty, 0); | ||||
| state.setProperty (memberNameProperty, document.getNonExistentMemberName (getMemberNameRoot()), 0); | state.setProperty (memberNameProperty, document.getNonExistentMemberName (getMemberNameRoot()), 0); | ||||
| state.setProperty (compBoundsProperty, | |||||
| getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||||
| Random::getSystemRandom().nextInt (100) + 100)).toString(), 0); | |||||
| const Rectangle<int> bounds (getDefaultSize().withPosition (Point<int> (Random::getSystemRandom().nextInt (100) + 100, | |||||
| Random::getSystemRandom().nextInt (100) + 100))); | |||||
| state.setProperty (compBoundsProperty, RectangleCoordinates (bounds).toString(), 0); | |||||
| } | } | ||||
| void ComponentTypeHandler::createPropertyEditors (ValueTree& state, ComponentDocument& document, | |||||
| Array <PropertyComponent*>& props) | |||||
| void ComponentTypeHandler::createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props) | |||||
| { | { | ||||
| props.add (new TextPropertyComponent (getValue (compBoundsProperty, state, document), "Bounds", 512, false)); | props.add (new TextPropertyComponent (getValue (compBoundsProperty, state, document), "Bounds", 512, false)); | ||||
| props.getLast()->setTooltip ("The component's position."); | props.getLast()->setTooltip ("The component's position."); | ||||
| @@ -100,7 +102,7 @@ public: | |||||
| juce_DeclareSingleton_SingleThreaded_Minimal (ComponentTypeManager); | juce_DeclareSingleton_SingleThreaded_Minimal (ComponentTypeManager); | ||||
| Component* createFromStoredType (const ValueTree& value) | |||||
| Component* createFromStoredType (ComponentDocument& document, const ValueTree& value) | |||||
| { | { | ||||
| ComponentTypeHandler* handler = getHandlerFor (value.getType()); | ComponentTypeHandler* handler = getHandlerFor (value.getType()); | ||||
| if (handler == 0) | if (handler == 0) | ||||
| @@ -108,7 +110,7 @@ public: | |||||
| Component* c = handler->createComponent(); | Component* c = handler->createComponent(); | ||||
| if (c != 0) | if (c != 0) | ||||
| handler->updateComponent (c, value); | |||||
| handler->updateComponent (document, c, value); | |||||
| return c; | return c; | ||||
| } | } | ||||
| @@ -331,7 +333,7 @@ void ComponentDocument::performNewComponentMenuItem (int menuResultCode) | |||||
| { | { | ||||
| ValueTree state (handler->getXmlTag()); | ValueTree state (handler->getXmlTag()); | ||||
| state.setProperty (idProperty, createAlphaNumericUID(), 0); | state.setProperty (idProperty, createAlphaNumericUID(), 0); | ||||
| handler->initialiseNewItem (state, *this); | |||||
| handler->initialiseNewItem (*this, state); | |||||
| getComponentGroup().addChild (state, -1, getUndoManager()); | getComponentGroup().addChild (state, -1, getUndoManager()); | ||||
| } | } | ||||
| @@ -368,13 +370,13 @@ const ValueTree ComponentDocument::getComponentWithMemberName (const String& nam | |||||
| return ValueTree::invalid; | return ValueTree::invalid; | ||||
| } | } | ||||
| Component* ComponentDocument::createComponent (int index) const | |||||
| Component* ComponentDocument::createComponent (int index) | |||||
| { | { | ||||
| const ValueTree v (getComponentGroup().getChild (index)); | const ValueTree v (getComponentGroup().getChild (index)); | ||||
| if (v.isValid()) | if (v.isValid()) | ||||
| { | { | ||||
| Component* c = ComponentTypeManager::getInstance()->createFromStoredType (v); | |||||
| Component* c = ComponentTypeManager::getInstance()->createFromStoredType (*this, v); | |||||
| c->getProperties().set (idProperty, v[idProperty]); | c->getProperties().set (idProperty, v[idProperty]); | ||||
| jassert (c->getProperties()[idProperty].toString().isNotEmpty()); | jassert (c->getProperties()[idProperty].toString().isNotEmpty()); | ||||
| return c; | return c; | ||||
| @@ -383,7 +385,46 @@ Component* ComponentDocument::createComponent (int index) const | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| void ComponentDocument::updateComponent (Component* comp) const | |||||
| //============================================================================== | |||||
| class ComponentMarkerResolver : public Coordinate::MarkerResolver | |||||
| { | |||||
| public: | |||||
| ComponentMarkerResolver (ComponentDocument& doc, const ValueTree& state_, Component* parentComponent_) | |||||
| : owner (doc), state (state_), parentComponent (parentComponent_) | |||||
| {} | |||||
| ~ComponentMarkerResolver() {} | |||||
| const Coordinate findMarker (const String& name, bool isHorizontal) | |||||
| { | |||||
| if (name == "left") return RectangleCoordinates (state [compBoundsProperty]).left; | |||||
| else if (name == "right") return RectangleCoordinates (state [compBoundsProperty]).right; | |||||
| else if (name == "top") return RectangleCoordinates (state [compBoundsProperty]).top; | |||||
| else if (name == "bottom") return RectangleCoordinates (state [compBoundsProperty]).bottom; | |||||
| else if (name == Coordinate::parentRightMarkerName) return Coordinate ((double) parentComponent->getWidth(), isHorizontal); | |||||
| else if (name == Coordinate::parentBottomMarkerName) return Coordinate ((double) parentComponent->getHeight(), isHorizontal); | |||||
| return Coordinate (isHorizontal); | |||||
| } | |||||
| private: | |||||
| ComponentDocument& owner; | |||||
| ValueTree state; | |||||
| Component* parentComponent; | |||||
| }; | |||||
| const RectangleCoordinates ComponentDocument::getCoordsFor (const ValueTree& state) const | |||||
| { | |||||
| return RectangleCoordinates (state [compBoundsProperty]); | |||||
| } | |||||
| Coordinate::MarkerResolver* ComponentDocument::createMarkerResolver (const ValueTree& state, Component* parentComponent) | |||||
| { | |||||
| jassert (parentComponent != 0); | |||||
| return new ComponentMarkerResolver (*this, state, parentComponent); | |||||
| } | |||||
| void ComponentDocument::updateComponent (Component* comp) | |||||
| { | { | ||||
| const ValueTree v (getComponentState (comp)); | const ValueTree v (getComponentState (comp)); | ||||
| @@ -393,7 +434,7 @@ void ComponentDocument::updateComponent (Component* comp) const | |||||
| jassert (handler != 0); | jassert (handler != 0); | ||||
| if (handler != 0) | if (handler != 0) | ||||
| handler->updateComponent (comp, v); | |||||
| handler->updateComponent (*this, comp, v); | |||||
| } | } | ||||
| } | } | ||||
| @@ -431,7 +472,7 @@ void ComponentDocument::getComponentProperties (Array <PropertyComponent*>& prop | |||||
| jassert (handler != 0); | jassert (handler != 0); | ||||
| if (handler != 0) | if (handler != 0) | ||||
| handler->createPropertyEditors (v, *this, props); | |||||
| handler->createPropertyEditors (*this, v, props); | |||||
| } | } | ||||
| } | } | ||||
| @@ -473,8 +514,10 @@ public: | |||||
| DragHandler (ComponentDocument& document_, | DragHandler (ComponentDocument& document_, | ||||
| const Array<Component*>& items, | const Array<Component*>& items, | ||||
| const MouseEvent& e, | const MouseEvent& e, | ||||
| const ResizableBorderComponent::Zone& zone_) | |||||
| : document (document_), | |||||
| const ResizableBorderComponent::Zone& zone_, | |||||
| Component* parentForOverlays) | |||||
| : parentComponent (0), | |||||
| document (document_), | |||||
| zone (zone_) | zone (zone_) | ||||
| { | { | ||||
| for (int i = 0; i < items.size(); ++i) | for (int i = 0; i < items.size(); ++i) | ||||
| @@ -482,14 +525,20 @@ public: | |||||
| Component* comp = items.getUnchecked(i); | Component* comp = items.getUnchecked(i); | ||||
| jassert (comp != 0); | jassert (comp != 0); | ||||
| parentComponentSize.setSize (comp->getParentWidth(), comp->getParentHeight()); | |||||
| jassert (! parentComponentSize.isEmpty()); | |||||
| if (parentComponent == 0) | |||||
| parentComponent = comp->getParentComponent(); | |||||
| const ValueTree v (document.getComponentState (comp)); | const ValueTree v (document.getComponentState (comp)); | ||||
| draggedComponents.add (v); | draggedComponents.add (v); | ||||
| PositionedRectangle pos (v [compBoundsProperty].toString()); | |||||
| originalPositions.add (pos.getRectangle (parentComponentSize)); | |||||
| Rectangle<int> pos; | |||||
| { | |||||
| RectangleCoordinates relativePos (v [compBoundsProperty].toString()); | |||||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (v, parentComponent)); | |||||
| pos = relativePos.resolve (*markers); | |||||
| originalPositions.add (pos); | |||||
| } | |||||
| const Rectangle<float> floatPos ((float) pos.getX(), (float) pos.getY(), | const Rectangle<float> floatPos ((float) pos.getX(), (float) pos.getY(), | ||||
| (float) pos.getWidth(), (float) pos.getHeight()); | (float) pos.getWidth(), (float) pos.getHeight()); | ||||
| @@ -525,18 +574,42 @@ public: | |||||
| { | { | ||||
| document.getUndoManager()->undoCurrentTransactionOnly(); | document.getUndoManager()->undoCurrentTransactionOnly(); | ||||
| for (int i = 0; i < draggedComponents.size(); ++i) | |||||
| move (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i)); | |||||
| for (int n = 50;;) | |||||
| { | |||||
| // Need to repeatedly apply the new positions until they all settle down, in case some of | |||||
| // the coords are relative to each other.. | |||||
| bool anyUpdated = false; | |||||
| for (int i = 0; i < draggedComponents.size(); ++i) | |||||
| if (dragItem (draggedComponents.getReference(i), e.getOffsetFromDragStart(), originalPositions.getReference(i))) | |||||
| anyUpdated = true; | |||||
| if (! anyUpdated) | |||||
| break; | |||||
| if (--n == 0) | |||||
| { | |||||
| jassertfalse; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void move (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||||
| bool dragItem (ValueTree& v, const Point<int>& distance, const Rectangle<int>& originalPos) | |||||
| { | { | ||||
| Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||||
| const Rectangle<int> newBounds (zone.resizeRectangleBy (originalPos, distance)); | |||||
| RectangleCoordinates pr (v [compBoundsProperty].toString()); | |||||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (v, parentComponent)); | |||||
| PositionedRectangle pr (v [compBoundsProperty].toString()); | |||||
| pr.updateFrom (newBounds, parentComponentSize); | |||||
| pr.moveToAbsolute (newBounds, *markers); | |||||
| const String newBoundsString (pr.toString()); | |||||
| if (v[compBoundsProperty] == newBoundsString) | |||||
| return false; | |||||
| v.setProperty (compBoundsProperty, pr.toString(), document.getUndoManager()); | |||||
| v.setProperty (compBoundsProperty, newBoundsString, document.getUndoManager()); | |||||
| return true; | |||||
| } | } | ||||
| const Array<float> getVerticalSnapPositions (const Point<int>& distance) const | const Array<float> getVerticalSnapPositions (const Point<int>& distance) const | ||||
| @@ -558,7 +631,7 @@ public: | |||||
| } | } | ||||
| private: | private: | ||||
| Rectangle<int> parentComponentSize; | |||||
| Component* parentComponent; | |||||
| ComponentDocument& document; | ComponentDocument& document; | ||||
| Array <ValueTree> draggedComponents; | Array <ValueTree> draggedComponents; | ||||
| Array <Rectangle<int> > originalPositions; | Array <Rectangle<int> > originalPositions; | ||||
| @@ -566,9 +639,10 @@ private: | |||||
| const ResizableBorderComponent::Zone zone; | const ResizableBorderComponent::Zone zone; | ||||
| }; | }; | ||||
| void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, const ResizableBorderComponent::Zone& zone) | |||||
| void ComponentDocument::beginDrag (const Array<Component*>& items, const MouseEvent& e, | |||||
| Component* parentForOverlays, const ResizableBorderComponent::Zone& zone) | |||||
| { | { | ||||
| dragger = new DragHandler (*this, items, e, zone); | |||||
| dragger = new DragHandler (*this, items, e, zone, parentForOverlays); | |||||
| } | } | ||||
| void ComponentDocument::continueDrag (const MouseEvent& e) | void ComponentDocument::continueDrag (const MouseEvent& e) | ||||
| @@ -55,19 +55,21 @@ public: | |||||
| int getNumComponents() const; | int getNumComponents() const; | ||||
| const ValueTree getComponent (int index) const; | const ValueTree getComponent (int index) const; | ||||
| const ValueTree getComponentWithMemberName (const String& name) const; | const ValueTree getComponentWithMemberName (const String& name) const; | ||||
| Component* createComponent (int index) const; | |||||
| void updateComponent (Component* comp) const; | |||||
| Component* createComponent (int index); | |||||
| void updateComponent (Component* comp); | |||||
| bool containsComponent (Component* comp) const; | bool containsComponent (Component* comp) const; | ||||
| const ValueTree getComponentState (Component* comp) const; | const ValueTree getComponentState (Component* comp) const; | ||||
| void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | void getComponentProperties (Array <PropertyComponent*>& props, Component* comp); | ||||
| bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | bool isStateForComponent (const ValueTree& storedState, Component* comp) const; | ||||
| Coordinate::MarkerResolver* createMarkerResolver (const ValueTree& state, Component* parentComponent); | |||||
| const RectangleCoordinates getCoordsFor (const ValueTree& state) const; | |||||
| void addNewComponentMenuItems (PopupMenu& menu) const; | void addNewComponentMenuItems (PopupMenu& menu) const; | ||||
| void performNewComponentMenuItem (int menuResultCode); | void performNewComponentMenuItem (int menuResultCode); | ||||
| //============================================================================== | //============================================================================== | ||||
| void beginDrag (const Array<Component*>& items, const MouseEvent& e, | void beginDrag (const Array<Component*>& items, const MouseEvent& e, | ||||
| const ResizableBorderComponent::Zone& zone); | |||||
| Component* parentForOverlays, const ResizableBorderComponent::Zone& zone); | |||||
| void continueDrag (const MouseEvent& e); | void continueDrag (const MouseEvent& e); | ||||
| void endDrag (const MouseEvent& e); | void endDrag (const MouseEvent& e); | ||||
| @@ -114,9 +116,9 @@ public: | |||||
| virtual Component* createComponent() = 0; | virtual Component* createComponent() = 0; | ||||
| virtual const Rectangle<int> getDefaultSize() = 0; | virtual const Rectangle<int> getDefaultSize() = 0; | ||||
| virtual void updateComponent (Component* comp, const ValueTree& state); | |||||
| virtual void initialiseNewItem (ValueTree& state, ComponentDocument& document); | |||||
| virtual void createPropertyEditors (ValueTree& state, ComponentDocument& document, Array <PropertyComponent*>& props); | |||||
| virtual void updateComponent (ComponentDocument& document, Component* comp, const ValueTree& state); | |||||
| virtual void initialiseNewItem (ComponentDocument& document, ValueTree& state); | |||||
| virtual void createPropertyEditors (ComponentDocument& document, ValueTree& state, Array <PropertyComponent*>& props); | |||||
| Value getValue (const var::identifier& name, ValueTree& state, ComponentDocument& document) const; | Value getValue (const var::identifier& name, ValueTree& state, ComponentDocument& document) const; | ||||
| @@ -27,6 +27,134 @@ | |||||
| #include "jucer_ComponentEditor.h" | #include "jucer_ComponentEditor.h" | ||||
| //============================================================================== | |||||
| class SizeGuideComponent : public Component, | |||||
| public ComponentListener | |||||
| { | |||||
| public: | |||||
| enum Type | |||||
| { | |||||
| left, right, top, bottom | |||||
| }; | |||||
| SizeGuideComponent (ComponentDocument& document_, const ValueTree& state_, Component* component_, | |||||
| Component* parentForOverlays, Type type_) | |||||
| : document (document_), state (state_), component (component_), type (type_), | |||||
| font (10.0f) | |||||
| { | |||||
| component->addComponentListener (this); | |||||
| setAlwaysOnTop (true); | |||||
| parentForOverlays->addAndMakeVisible (this); | |||||
| updatePosition(); | |||||
| } | |||||
| ~SizeGuideComponent() | |||||
| { | |||||
| if (component != 0) | |||||
| component->removeComponentListener (this); | |||||
| } | |||||
| void updatePosition() | |||||
| { | |||||
| RectangleCoordinates coords (document.getCoordsFor (state)); | |||||
| Coordinate coord (false); | |||||
| bool isHorizontal = false; | |||||
| switch (type) | |||||
| { | |||||
| case left: coord = coords.left; isHorizontal = true; break; | |||||
| case right: coord = coords.right; isHorizontal = true; break; | |||||
| case top: coord = coords.top; break; | |||||
| case bottom: coord = coords.bottom; break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| setName (coord.toString()); | |||||
| ScopedPointer<Coordinate::MarkerResolver> markers (document.createMarkerResolver (state, component->getParentComponent())); | |||||
| int anchor1 = coord.getAnchorPoint1().resolve (*markers); | |||||
| int anchor2 = coord.getAnchorPoint2().resolve (*markers); | |||||
| Point<int> p1, p2; | |||||
| switch (type) | |||||
| { | |||||
| case left: p1 = Point<int> (component->getX(), component->getY() + component->proportionOfHeight (0.33f)); | |||||
| p2 = Point<int> (anchor1, p1.getY()); break; | |||||
| case right: p1 = Point<int> (component->getRight(), component->getY() + component->proportionOfHeight (0.66f)); | |||||
| p2 = Point<int> (anchor1, p1.getY()); break; | |||||
| case top: p1 = Point<int> (component->getX() + component->proportionOfWidth (0.33f), component->getY()); | |||||
| p2 = Point<int> (p1.getX(), anchor1); break; | |||||
| case bottom: p1 = Point<int> (component->getX() + component->proportionOfWidth (0.66f), component->getBottom()); | |||||
| p2 = Point<int> (p1.getX(), anchor1); break; | |||||
| default: jassertfalse; break; | |||||
| } | |||||
| Rectangle<int> bounds (Rectangle<int> (p1, p2).expanded (4, 4)); | |||||
| Point<int> textPos ((p1.getX() + p2.getX()) / 2, | |||||
| (p1.getY() + p2.getY()) / 2); | |||||
| int textW = (int) font.getStringWidth (getName()); | |||||
| int textH = (int) font.getHeight(); | |||||
| Rectangle<int> textRect (textPos.getX() - textW / 2, textPos.getY() - textH / 2, textW, textH); | |||||
| if (isHorizontal) | |||||
| textRect = textRect - Point<int> (0, textH / 2 + 4); | |||||
| bounds = bounds.getUnion (textRect); | |||||
| setBounds (bounds); | |||||
| lineEnd1 = p1 - bounds.getPosition(); | |||||
| lineEnd2 = p2 - bounds.getPosition(); | |||||
| textArea = textRect - bounds.getPosition(); | |||||
| repaint(); | |||||
| } | |||||
| void paint (Graphics& g) | |||||
| { | |||||
| Path p; | |||||
| p.addLineSegment (lineEnd1.getX(), lineEnd1.getY(), lineEnd2.getX(), lineEnd2.getY(), 1.6f); | |||||
| const float startBlobSize = 2.0f; | |||||
| p.addEllipse (lineEnd1.getX() - startBlobSize, lineEnd1.getY() - startBlobSize, startBlobSize * 2.0f, startBlobSize * 2.0f); | |||||
| const float endBlobSize = 4.0f; | |||||
| p.addEllipse (lineEnd2.getX() - endBlobSize, lineEnd2.getY() - endBlobSize, endBlobSize * 2.0f, endBlobSize * 2.0f); | |||||
| g.setColour (Colours::black.withAlpha (0.3f)); | |||||
| g.fillPath (p); | |||||
| g.setFont (font); | |||||
| g.setColour (Colours::white); | |||||
| for (int y = -1; y <= 1; ++y) | |||||
| for (int x = -1; x <= 1; ++x) | |||||
| g.drawText (getName(), textArea.getX() + x, textArea.getY() + y, textArea.getWidth(), textArea.getHeight(), Justification::centred, 1); | |||||
| g.setColour (Colours::black); | |||||
| g.drawText (getName(), textArea.getX(), textArea.getY(), textArea.getWidth(), textArea.getHeight(), Justification::centred, 1); | |||||
| } | |||||
| void componentMovedOrResized (Component&, bool, bool) | |||||
| { | |||||
| updatePosition(); | |||||
| } | |||||
| void componentBeingDeleted (Component&) | |||||
| { | |||||
| setVisible (false); | |||||
| component = 0; | |||||
| } | |||||
| private: | |||||
| ComponentDocument& document; | |||||
| ValueTree state; | |||||
| Component* component; | |||||
| Type type; | |||||
| Font font; | |||||
| Point<int> lineEnd1, lineEnd2; | |||||
| Rectangle<int> textArea; | |||||
| }; | |||||
| //============================================================================== | //============================================================================== | ||||
| class ComponentEditor::Canvas : public Component, | class ComponentEditor::Canvas : public Component, | ||||
| public ValueTree::Listener, | public ValueTree::Listener, | ||||
| @@ -116,10 +244,8 @@ public: | |||||
| c = doc.createComponent (i); | c = doc.createComponent (i); | ||||
| componentHolder->addAndMakeVisible (c); | componentHolder->addAndMakeVisible (c); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| doc.updateComponent (c); | |||||
| } | |||||
| doc.updateComponent (c); | |||||
| } | } | ||||
| startTimer (500); | startTimer (500); | ||||
| @@ -175,9 +301,57 @@ public: | |||||
| getDocument().beginNewTransaction(); | getDocument().beginNewTransaction(); | ||||
| } | } | ||||
| void mouseMove (const MouseEvent& e) | |||||
| { | |||||
| updateDragZone (e.getPosition()); | |||||
| } | |||||
| void mouseDown (const MouseEvent& e) | |||||
| { | |||||
| updateDragZone (e.getPosition()); | |||||
| dragStartSize = getBounds(); | |||||
| } | |||||
| void mouseDrag (const MouseEvent& e) | |||||
| { | |||||
| if (dragZone.isDraggingRightEdge() || dragZone.isDraggingBottomEdge()) | |||||
| { | |||||
| showSizeGuides(); | |||||
| setSize (jmax (0, dragStartSize.getWidth() + e.getDistanceFromDragStartX()), | |||||
| jmax (0, dragStartSize.getHeight() + e.getDistanceFromDragStartY())); | |||||
| } | |||||
| } | |||||
| void mouseUp (const MouseEvent& e) | |||||
| { | |||||
| hideSizeGuides(); | |||||
| updateDragZone (e.getPosition()); | |||||
| } | |||||
| void updateDragZone (const Point<int>& p) | |||||
| { | |||||
| ResizableBorderComponent::Zone newZone | |||||
| = ResizableBorderComponent::Zone::fromPositionOnBorder (getLocalBounds(), | |||||
| BorderSize (borderThickness), p); | |||||
| newZone = ResizableBorderComponent::Zone (newZone.getZoneFlags() | |||||
| & (ResizableBorderComponent::Zone::right | ResizableBorderComponent::Zone::bottom)); | |||||
| if (dragZone != newZone) | |||||
| { | |||||
| dragZone = newZone; | |||||
| setMouseCursor (newZone.getMouseCursor()); | |||||
| } | |||||
| } | |||||
| void showSizeGuides() { overlay->showSizeGuides(); } | |||||
| void hideSizeGuides() { overlay->hideSizeGuides(); } | |||||
| private: | private: | ||||
| ComponentEditor& editor; | ComponentEditor& editor; | ||||
| const int borderThickness; | const int borderThickness; | ||||
| ResizableBorderComponent::Zone dragZone; | |||||
| Rectangle<int> dragStartSize; | |||||
| //============================================================================== | //============================================================================== | ||||
| class ComponentResizeFrame : public Component, | class ComponentResizeFrame : public Component, | ||||
| @@ -230,18 +404,23 @@ private: | |||||
| if (component != 0) | if (component != 0) | ||||
| { | { | ||||
| updateDragZone (e.getPosition()); | updateDragZone (e.getPosition()); | ||||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, dragZone); | |||||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), dragZone); | |||||
| } | } | ||||
| } | } | ||||
| void mouseDrag (const MouseEvent& e) | void mouseDrag (const MouseEvent& e) | ||||
| { | { | ||||
| if (component != 0) | if (component != 0) | ||||
| { | |||||
| canvas.showSizeGuides(); | |||||
| canvas.getDocument().continueDrag (e); | canvas.getDocument().continueDrag (e); | ||||
| } | |||||
| } | } | ||||
| void mouseUp (const MouseEvent& e) | void mouseUp (const MouseEvent& e) | ||||
| { | { | ||||
| canvas.hideSizeGuides(); | |||||
| if (component != 0) | if (component != 0) | ||||
| canvas.getDocument().endDrag (e); | canvas.getDocument().endDrag (e); | ||||
| @@ -261,11 +440,29 @@ private: | |||||
| uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); } | uint32 getTargetComponentUID() const { return component == 0 ? 0 : component->getComponentUID(); } | ||||
| void showSizeGuides() | |||||
| { | |||||
| if (sizeGuides.size() == 0) | |||||
| { | |||||
| const ValueTree v (canvas.getDocument().getComponentState (component)); | |||||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::left)); | |||||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::right)); | |||||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::top)); | |||||
| sizeGuides.add (new SizeGuideComponent (canvas.getDocument(), v, component, getParentComponent(), SizeGuideComponent::bottom)); | |||||
| } | |||||
| } | |||||
| void hideSizeGuides() | |||||
| { | |||||
| sizeGuides.clear(); | |||||
| } | |||||
| private: | private: | ||||
| Canvas& canvas; | Canvas& canvas; | ||||
| Component::SafePointer<Component> component; | Component::SafePointer<Component> component; | ||||
| ResizableBorderComponent::Zone dragZone; | ResizableBorderComponent::Zone dragZone; | ||||
| const int borderThickness; | const int borderThickness; | ||||
| OwnedArray <SizeGuideComponent> sizeGuides; | |||||
| const Rectangle<int> getCentreArea() const | const Rectangle<int> getCentreArea() const | ||||
| { | { | ||||
| @@ -354,16 +551,19 @@ private: | |||||
| { | { | ||||
| isDraggingClickedComp = true; | isDraggingClickedComp = true; | ||||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); | ||||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, | |||||
| canvas.getDocument().beginDrag (canvas.getSelectedComps(), e, getParentComponent(), | |||||
| ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); | ||||
| } | } | ||||
| canvas.getDocument().continueDrag (e); | canvas.getDocument().continueDrag (e); | ||||
| showSizeGuides(); | |||||
| } | } | ||||
| } | } | ||||
| void mouseUp (const MouseEvent& e) | void mouseUp (const MouseEvent& e) | ||||
| { | { | ||||
| hideSizeGuides(); | |||||
| if (lasso != 0) | if (lasso != 0) | ||||
| { | { | ||||
| lasso->endLasso(); | lasso->endLasso(); | ||||
| @@ -377,6 +577,8 @@ private: | |||||
| if (! isDraggingClickedComp) | if (! isDraggingClickedComp) | ||||
| canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); | canvas.getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, ! e.mouseWasClicked(), mouseDownResult); | ||||
| } | } | ||||
| canvas.getDocument().endDrag (e); | |||||
| } | } | ||||
| void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height) | void findLassoItemsInArea (Array <ComponentDocument::SelectedItems::ItemType>& itemsFound, int x, int y, int width, int height) | ||||
| @@ -403,6 +605,26 @@ private: | |||||
| Desktop::getInstance().getMainMouseSource().triggerFakeMove(); | Desktop::getInstance().getMainMouseSource().triggerFakeMove(); | ||||
| } | } | ||||
| void showSizeGuides() | |||||
| { | |||||
| for (int i = getNumChildComponents(); --i >= 0;) | |||||
| { | |||||
| ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i)); | |||||
| if (resizer != 0) | |||||
| resizer->showSizeGuides(); | |||||
| } | |||||
| } | |||||
| void hideSizeGuides() | |||||
| { | |||||
| for (int i = getNumChildComponents(); --i >= 0;) | |||||
| { | |||||
| ComponentResizeFrame* resizer = dynamic_cast <ComponentResizeFrame*> (getChildComponent(i)); | |||||
| if (resizer != 0) | |||||
| resizer->hideSizeGuides(); | |||||
| } | |||||
| } | |||||
| private: | private: | ||||
| Canvas& canvas; | Canvas& canvas; | ||||
| ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso; | ScopedPointer <LassoComponent <ComponentDocument::SelectedItems::ItemType> > lasso; | ||||
| @@ -505,152 +505,326 @@ int indexOfLineStartingWith (const StringArray& lines, const String& text, int s | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| RelativePosition::RelativePosition() | |||||
| : value (0), isRelative (false) | |||||
| 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 isHorizontal_) | |||||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||||
| { | |||||
| } | |||||
| Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal_) | |||||
| : value (absoluteDistanceFromOrigin), isProportion (false), isHorizontal (isHorizontal_) | |||||
| { | |||||
| } | |||||
| Coordinate::Coordinate (double absoluteDistance, const String& source, bool isHorizontal_) | |||||
| : anchor1 (source), value (absoluteDistance), isProportion (false), isHorizontal (isHorizontal_) | |||||
| { | |||||
| } | |||||
| Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool isHorizontal_) | |||||
| : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), isHorizontal (isHorizontal_) | |||||
| { | { | ||||
| } | } | ||||
| RelativePosition::RelativePosition (double absoluteDistanceFromOrigin) | |||||
| : value (absoluteDistanceFromOrigin), isRelative (false) | |||||
| Coordinate::~Coordinate() | |||||
| { | { | ||||
| } | } | ||||
| RelativePosition::RelativePosition (double absoluteDistance, const String& source) | |||||
| : nameOfSource1 (source), value (absoluteDistance), isRelative (false) | |||||
| const Coordinate Coordinate::getAnchorPoint1() const | |||||
| { | { | ||||
| return Coordinate (0.0, anchor1, isHorizontal); | |||||
| } | } | ||||
| RelativePosition::RelativePosition (double relativeProportion, const String& pos1, const String& pos2) | |||||
| : nameOfSource1 (pos1), nameOfSource2 (pos2), value (relativeProportion), isRelative (true) | |||||
| const Coordinate Coordinate::getAnchorPoint2() const | |||||
| { | { | ||||
| return Coordinate (0.0, anchor2, isHorizontal); | |||||
| } | } | ||||
| RelativePosition::~RelativePosition() | |||||
| bool Coordinate::isOrigin (const String& name) | |||||
| { | { | ||||
| return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName; | |||||
| } | } | ||||
| bool RelativePosition::isOrigin (const String& name) | |||||
| const String Coordinate::getOriginMarkerName() const | |||||
| { | { | ||||
| return name.isEmpty() || name == parentOriginMarkerName; | |||||
| return isHorizontal ? parentLeftMarkerName : parentTopMarkerName; | |||||
| } | } | ||||
| const String RelativePosition::checkName (const String& name) | |||||
| const String Coordinate::getExtentMarkerName() const | |||||
| { | { | ||||
| return name.isEmpty() ? parentOriginMarkerName : name; | |||||
| return isHorizontal ? parentRightMarkerName : parentBottomMarkerName; | |||||
| } | } | ||||
| double RelativePosition::getPosition (const String& name, PositionFinder& positionFinder) const | |||||
| const String Coordinate::checkName (const String& name) const | |||||
| { | |||||
| return name.isEmpty() ? getOriginMarkerName() : name; | |||||
| } | |||||
| double Coordinate::getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const | |||||
| { | { | ||||
| if (isOrigin (name)) | if (isOrigin (name)) | ||||
| return 0.0; | return 0.0; | ||||
| RelativePosition* const pos = positionFinder.findPosition (nameOfSource1); | |||||
| return markerResolver.findMarker (name, isHorizontal) | |||||
| .resolve (markerResolver, recursionCounter + 1); | |||||
| } | |||||
| if (pos != 0) | |||||
| return pos->resolve (positionFinder); | |||||
| struct RecursivePositionException | |||||
| { | |||||
| }; | |||||
| double Coordinate::resolve (MarkerResolver& markerResolver, int recursionCounter) const | |||||
| { | |||||
| if (recursionCounter > 100) | |||||
| { | |||||
| jassertfalse | |||||
| throw RecursivePositionException(); | |||||
| } | |||||
| const double pos1 = getPosition (anchor1, markerResolver, recursionCounter); | |||||
| return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value | |||||
| : pos1 + value; | |||||
| } | |||||
| double Coordinate::resolve (MarkerResolver& markerResolver) const | |||||
| { | |||||
| try | |||||
| { | |||||
| return resolve (markerResolver, 0); | |||||
| } | |||||
| catch (RecursivePositionException&) | |||||
| {} | |||||
| jassertfalse; | |||||
| return 0.0; | return 0.0; | ||||
| } | } | ||||
| double RelativePosition::resolve (PositionFinder& positionFinder) const | |||||
| void Coordinate::moveToAbsolute (double newPos, MarkerResolver& markerResolver) | |||||
| { | { | ||||
| const double pos1 = getPos1 (positionFinder); | |||||
| try | |||||
| { | |||||
| const double pos1 = getPosition (anchor1, markerResolver, 0); | |||||
| if (isProportion) | |||||
| { | |||||
| const double size = getPosition (anchor2, markerResolver, 0) - pos1; | |||||
| return isRelative ? pos1 + (getPos2 (positionFinder) - pos1) * value | |||||
| : pos1 + value; | |||||
| if (size != 0) | |||||
| value = (newPos - pos1) / size; | |||||
| } | |||||
| else | |||||
| { | |||||
| value = newPos - pos1; | |||||
| } | |||||
| } | |||||
| catch (RecursivePositionException&) | |||||
| {} | |||||
| } | } | ||||
| void RelativePosition::moveToAbsolute (double newPos, PositionFinder& positionFinder) | |||||
| bool Coordinate::isRecursive (MarkerResolver& markerResolver) const | |||||
| { | { | ||||
| const double pos1 = getPos1 (positionFinder); | |||||
| try | |||||
| { | |||||
| resolve (markerResolver, 0); | |||||
| } | |||||
| catch (RecursivePositionException&) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| if (isRelative) | |||||
| value = (newPos - pos1) / (getPos2 (positionFinder) - pos1); | |||||
| else | |||||
| value = newPos - pos1; | |||||
| return false; | |||||
| } | } | ||||
| RelativePosition::RelativePosition (const String& stringVersion) | |||||
| void Coordinate::skipWhitespace (const String& s, int& i) | |||||
| { | { | ||||
| jassertfalse //todo | |||||
| while (CharacterFunctions::isWhitespace (s[i])) | |||||
| ++i; | |||||
| } | } | ||||
| const String RelativePosition::toString (int decimalPlaces) const | |||||
| const String Coordinate::readMarkerName (const String& s, int& i) | |||||
| { | { | ||||
| if (isRelative) | |||||
| skipWhitespace (s, i); | |||||
| if (CharacterFunctions::isLetter (s[i]) || s[i] == '_') | |||||
| { | { | ||||
| const String percent (value * 100.0, 2); | |||||
| int start = i; | |||||
| while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.') | |||||
| ++i; | |||||
| if (isOrigin (nameOfSource1)) | |||||
| return s.substring (start, i); | |||||
| } | |||||
| return String::empty; | |||||
| } | |||||
| double Coordinate::readNumber (const String& s, int& i) | |||||
| { | |||||
| skipWhitespace (s, i); | |||||
| int start = i; | |||||
| if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-') | |||||
| ++i; | |||||
| while (CharacterFunctions::isDigit (s[i]) || s[i] == '.') | |||||
| ++i; | |||||
| if ((s[i] == 'e' || s[i] == 'E') | |||||
| && (CharacterFunctions::isDigit (s[i + 1]) | |||||
| || s[i + 1] == '-' | |||||
| || s[i + 1] == '+')) | |||||
| { | |||||
| i += 2; | |||||
| while (CharacterFunctions::isDigit (s[i])) | |||||
| ++i; | |||||
| } | |||||
| const double value = s.substring (start, i).getDoubleValue(); | |||||
| while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',') | |||||
| ++i; | |||||
| return value; | |||||
| } | |||||
| Coordinate::Coordinate (const String& s, bool isHorizontal_) | |||||
| : value (0), isProportion (false), isHorizontal (isHorizontal_) | |||||
| { | |||||
| int i = 0; | |||||
| anchor1 = readMarkerName (s, i); | |||||
| if (anchor1.isNotEmpty()) | |||||
| { | |||||
| skipWhitespace (s, i); | |||||
| if (s[i] == '+') | |||||
| value = readNumber (s, ++i); | |||||
| else if (s[i] == '-') | |||||
| value = -readNumber (s, ++i); | |||||
| } | |||||
| else | |||||
| { | |||||
| value = readNumber (s, i); | |||||
| skipWhitespace (s, i); | |||||
| if (s[i] == '%') | |||||
| { | |||||
| isProportion = true; | |||||
| value /= 100.0; | |||||
| skipWhitespace (s, ++i); | |||||
| if (s[i] == '*') | |||||
| { | |||||
| anchor1 = readMarkerName (s, ++i); | |||||
| skipWhitespace (s, i); | |||||
| if (s[i] == '-' && s[i + 1] == '>') | |||||
| { | |||||
| i += 2; | |||||
| anchor2 = readMarkerName (s, i); | |||||
| } | |||||
| else | |||||
| { | |||||
| anchor2 = anchor1; | |||||
| anchor1 = getOriginMarkerName(); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| anchor1 = getOriginMarkerName(); | |||||
| anchor2 = getExtentMarkerName(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| const String Coordinate::toString() const | |||||
| { | |||||
| if (isProportion) | |||||
| { | |||||
| const String percent (value * 100.0); | |||||
| if (isOrigin (anchor1)) | |||||
| { | { | ||||
| if (nameOfSource2 == parentExtentMarkerName) | |||||
| if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName) | |||||
| return percent + "%"; | return percent + "%"; | ||||
| else | else | ||||
| return percent + "% of " + checkName (nameOfSource2); | |||||
| return percent + "% * " + checkName (anchor2); | |||||
| } | } | ||||
| else | else | ||||
| return percent + "% of " + checkName (nameOfSource1) + " to " + checkName (nameOfSource2); | |||||
| return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| if (isOrigin (nameOfSource1)) | |||||
| return String (value, decimalPlaces); | |||||
| else if (value != 0) | |||||
| return checkName (nameOfSource1) + " + " + String (value, decimalPlaces); | |||||
| if (isOrigin (anchor1)) | |||||
| return String (value); | |||||
| else if (value > 0) | |||||
| return checkName (anchor1) + " + " + String (value); | |||||
| else if (value < 0) | |||||
| return checkName (anchor1) + " - " + String (-value); | |||||
| else | else | ||||
| return checkName (nameOfSource1); | |||||
| return checkName (anchor1); | |||||
| } | } | ||||
| } | } | ||||
| const char* RelativePosition::parentOriginMarkerName = "origin"; | |||||
| const char* RelativePosition::parentExtentMarkerName = "size"; | |||||
| //============================================================================== | //============================================================================== | ||||
| RelativeRectangle::RelativeRectangle() | |||||
| RectangleCoordinates::RectangleCoordinates() | |||||
| : left (true), right (true), top (false), bottom (false) | |||||
| { | { | ||||
| } | } | ||||
| RelativeRectangle::RelativeRectangle (const Rectangle<int>& rect) | |||||
| : left (rect.getX()), | |||||
| right (rect.getRight()), | |||||
| top (rect.getY()), | |||||
| bottom (rect.getBottom()) | |||||
| RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect) | |||||
| : left (rect.getX(), true), | |||||
| right (rect.getWidth(), "left", true), | |||||
| top (rect.getY(), false), | |||||
| bottom (rect.getHeight(), "top", false) | |||||
| { | { | ||||
| } | } | ||||
| RelativeRectangle::RelativeRectangle (const String& stringVersion) | |||||
| RectangleCoordinates::RectangleCoordinates (const String& stringVersion) | |||||
| : left (true), right (true), top (false), bottom (false) | |||||
| { | { | ||||
| jassertfalse // todo | |||||
| StringArray tokens; | |||||
| tokens.addTokens (stringVersion, ",", String::empty); | |||||
| left = Coordinate (tokens [0], true); | |||||
| top = Coordinate (tokens [1], false); | |||||
| right = Coordinate (tokens [2], true); | |||||
| bottom = Coordinate (tokens [3], false); | |||||
| } | } | ||||
| const Rectangle<int> RelativeRectangle::resolve (RelativePosition::PositionFinder& positionFinder) const | |||||
| bool RectangleCoordinates::isRecursive (Coordinate::MarkerResolver& markerResolver) const | |||||
| { | { | ||||
| const int l = roundToInt (left.resolve (positionFinder)); | |||||
| const int r = roundToInt (right.resolve (positionFinder)); | |||||
| const int t = roundToInt (top.resolve (positionFinder)); | |||||
| const int b = roundToInt (bottom.resolve (positionFinder)); | |||||
| return left.isRecursive (markerResolver) || right.isRecursive (markerResolver) | |||||
| || top.isRecursive (markerResolver) || bottom.isRecursive (markerResolver); | |||||
| } | |||||
| const Rectangle<int> RectangleCoordinates::resolve (Coordinate::MarkerResolver& markerResolver) 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)); | |||||
| return Rectangle<int> (l, t, r - l, b - t); | return Rectangle<int> (l, t, r - l, b - t); | ||||
| } | } | ||||
| void RelativeRectangle::moveToAbsolute (const Rectangle<int>& newPos, RelativePosition::PositionFinder& positionFinder) | |||||
| void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver) | |||||
| { | { | ||||
| left.moveToAbsolute (newPos.getX(), positionFinder); | |||||
| right.moveToAbsolute (newPos.getRight(), positionFinder); | |||||
| top.moveToAbsolute (newPos.getY(), positionFinder); | |||||
| bottom.moveToAbsolute (newPos.getBottom(), positionFinder); | |||||
| // do it all again in case there were dependencies between some of the positions.. | |||||
| left.moveToAbsolute (newPos.getX(), positionFinder); | |||||
| right.moveToAbsolute (newPos.getRight(), positionFinder); | |||||
| top.moveToAbsolute (newPos.getY(), positionFinder); | |||||
| bottom.moveToAbsolute (newPos.getBottom(), positionFinder); | |||||
| left.moveToAbsolute (newPos.getX(), markerResolver); | |||||
| right.moveToAbsolute (newPos.getRight(), markerResolver); | |||||
| top.moveToAbsolute (newPos.getY(), markerResolver); | |||||
| bottom.moveToAbsolute (newPos.getBottom(), markerResolver); | |||||
| } | } | ||||
| const String RelativeRectangle::toString (int decimalPlaces) const | |||||
| const String RectangleCoordinates::toString() const | |||||
| { | { | ||||
| return left.toString (decimalPlaces) + ", " + top.toString (decimalPlaces) | |||||
| + ", " + right.toString (decimalPlaces) + ", " + bottom.toString (decimalPlaces); | |||||
| return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString(); | |||||
| } | } | ||||
| @@ -111,56 +111,113 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| class RelativePosition | |||||
| /** | |||||
| Holds a co-ordinate along the x or y axis, expressed either as an absolute | |||||
| position, or relative to other named marker positions. | |||||
| */ | |||||
| class Coordinate | |||||
| { | { | ||||
| public: | public: | ||||
| RelativePosition(); | |||||
| explicit RelativePosition (const String& stringVersion); | |||||
| explicit RelativePosition (double absoluteDistanceFromOrigin); | |||||
| RelativePosition (double absoluteDistance, const String& source); | |||||
| RelativePosition (double relativeProportion, const String& pos1, const String& pos2); | |||||
| ~RelativePosition(); | |||||
| class PositionFinder | |||||
| { | |||||
| public: | |||||
| virtual ~PositionFinder() {} | |||||
| virtual RelativePosition* findPosition (const String& name) = 0; | |||||
| }; | |||||
| //============================================================================== | |||||
| /** Creates a zero coordinate. */ | |||||
| Coordinate (bool isHorizontal); | |||||
| /** Recreates a coordinate from its stringified version. */ | |||||
| explicit Coordinate (const String& stringVersion, bool isHorizontal); | |||||
| /** Creates an absolute position from the parent origin. */ | |||||
| explicit Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal); | |||||
| /** Creates an absolute position relative to a named marker. */ | |||||
| Coordinate (double absolutePosition, const String& relativeToMarker, bool isHorizontal); | |||||
| const String getName() const { return name; } | |||||
| void setName (const String& newName) { name = newName; } | |||||
| /** Creates a relative position between two named markers. */ | |||||
| Coordinate (double relativePosition, const String& marker1, const String& marker2, bool isHorizontal); | |||||
| double resolve (PositionFinder& positionFinder) const; | |||||
| void moveToAbsolute (double newPos, PositionFinder& positionFinder); | |||||
| /** Destructor. */ | |||||
| ~Coordinate(); | |||||
| const String toString (int decimalPlaces) const; | |||||
| //============================================================================== | |||||
| /** | |||||
| Provides an interface for looking up the position of a named marker. | |||||
| */ | |||||
| class MarkerResolver | |||||
| { | |||||
| public: | |||||
| virtual ~MarkerResolver() {} | |||||
| virtual const Coordinate findMarker (const String& name, bool isHorizontal) = 0; | |||||
| }; | |||||
| static const char* parentOriginMarkerName; | |||||
| static const char* parentExtentMarkerName; | |||||
| /** Calculates the absolute position of this co-ordinate. */ | |||||
| double resolve (MarkerResolver& markerResolver) const; | |||||
| /** Returns true if this co-ordinate is expressed in terms of markers that form a recursive loop. */ | |||||
| bool isRecursive (MarkerResolver& markerResolver) const; | |||||
| /** Changes the value of this marker to make it resolve to the specified position. */ | |||||
| void moveToAbsolute (double newPos, MarkerResolver& markerResolver); | |||||
| const Coordinate getAnchorPoint1() const; | |||||
| const Coordinate getAnchorPoint2() const; | |||||
| //============================================================================== | |||||
| /* | |||||
| Position string formats: | |||||
| 123 = absolute pixels from parent origin | |||||
| marker | |||||
| marker + 123 | |||||
| marker - 123 | |||||
| 50% = percentage between parent origin and parent extent | |||||
| 50% * marker = percentage between parent origin and marker | |||||
| 50% * marker1 -> marker2 = percentage between two markers | |||||
| standard marker names: | |||||
| "origin" = parent origin | |||||
| "size" = parent right or bottom | |||||
| "top", "left", "bottom", "right" = refer to the component's own left, right, top and bottom. | |||||
| */ | |||||
| const String toString() const; | |||||
| //============================================================================== | |||||
| static const char* parentLeftMarkerName; | |||||
| static const char* parentRightMarkerName; | |||||
| static const char* parentTopMarkerName; | |||||
| static const char* parentBottomMarkerName; | |||||
| private: | private: | ||||
| String name, nameOfSource1, nameOfSource2; | |||||
| //============================================================================== | |||||
| String anchor1, anchor2; | |||||
| double value; | double value; | ||||
| bool isRelative; | |||||
| bool isProportion, isHorizontal; | |||||
| double getPos1 (PositionFinder& positionFinder) const { return getPosition (nameOfSource1, positionFinder); } | |||||
| double getPos2 (PositionFinder& positionFinder) const { return getPosition (nameOfSource2, positionFinder); } | |||||
| double getPosition (const String& name, PositionFinder& positionFinder) const; | |||||
| static const String checkName (const String& name); | |||||
| double resolve (MarkerResolver& markerResolver, int recursionCounter) const; | |||||
| double getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const; | |||||
| const String checkName (const String& name) const; | |||||
| const String getOriginMarkerName() const; | |||||
| const String getExtentMarkerName() 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); | |||||
| }; | }; | ||||
| class RelativeRectangle | |||||
| //============================================================================== | |||||
| /** | |||||
| Describes a rectangle as a set of Coordinate values. | |||||
| */ | |||||
| class RectangleCoordinates | |||||
| { | { | ||||
| public: | public: | ||||
| RelativeRectangle(); | |||||
| explicit RelativeRectangle (const Rectangle<int>& rect); | |||||
| explicit RelativeRectangle (const String& stringVersion); | |||||
| const Rectangle<int> resolve (RelativePosition::PositionFinder& positionFinder) const; | |||||
| void moveToAbsolute (const Rectangle<int>& newPos, RelativePosition::PositionFinder& positionFinder); | |||||
| const String toString (int decimalPlaces) const; | |||||
| RelativePosition left, right, top, bottom; | |||||
| //============================================================================== | |||||
| RectangleCoordinates(); | |||||
| explicit RectangleCoordinates (const Rectangle<int>& rect); | |||||
| explicit RectangleCoordinates (const String& stringVersion); | |||||
| //============================================================================== | |||||
| const Rectangle<int> resolve (Coordinate::MarkerResolver& markerResolver) const; | |||||
| bool isRecursive (Coordinate::MarkerResolver& markerResolver) const; | |||||
| void moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver); | |||||
| const String toString() const; | |||||
| Coordinate left, right, top, bottom; | |||||
| }; | }; | ||||
| @@ -60932,13 +60932,13 @@ const Rectangle<int> ResizableBorderComponent::Zone::resizeRectangleBy (Rectangl | |||||
| b.setLeft (b.getX() + offset.getX()); | b.setLeft (b.getX() + offset.getX()); | ||||
| if (isDraggingRightEdge()) | if (isDraggingRightEdge()) | ||||
| b.setWidth (b.getWidth() + offset.getX()); | |||||
| b.setWidth (jmax (0, b.getWidth() + offset.getX())); | |||||
| if (isDraggingTopEdge()) | if (isDraggingTopEdge()) | ||||
| b.setTop (b.getY() + offset.getY()); | b.setTop (b.getY() + offset.getY()); | ||||
| if (isDraggingBottomEdge()) | if (isDraggingBottomEdge()) | ||||
| b.setHeight (b.getHeight() + offset.getY()); | |||||
| b.setHeight (jmax (0, b.getHeight() + offset.getY())); | |||||
| return b; | return b; | ||||
| } | } | ||||
| @@ -211817,6 +211817,9 @@ void InterProcessLock::exit() | |||||
| { | { | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| // Trying to release the lock too many times! | |||||
| jassert (pimpl != 0); | |||||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | if (pimpl != 0 && --(pimpl->refCount) == 0) | ||||
| pimpl = 0; | pimpl = 0; | ||||
| } | } | ||||
| @@ -228044,6 +228047,9 @@ void InterProcessLock::exit() | |||||
| { | { | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| // Trying to release the lock too many times! | |||||
| jassert (pimpl != 0); | |||||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | if (pimpl != 0 && --(pimpl->refCount) == 0) | ||||
| pimpl = 0; | pimpl = 0; | ||||
| } | } | ||||
| @@ -238095,6 +238101,9 @@ void InterProcessLock::exit() | |||||
| { | { | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| // Trying to release the lock too many times! | |||||
| jassert (pimpl != 0); | |||||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | if (pimpl != 0 && --(pimpl->refCount) == 0) | ||||
| pimpl = 0; | pimpl = 0; | ||||
| } | } | ||||
| @@ -8723,13 +8723,16 @@ public: | |||||
| { | { | ||||
| public: | public: | ||||
| inline explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lock.enter(); } | |||||
| explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lockWasSuccessful = lock.enter(); } | |||||
| inline ~ScopedLockType() { lock_.exit(); } | inline ~ScopedLockType() { lock_.exit(); } | ||||
| bool isLocked() const throw() { return lockWasSuccessful; } | |||||
| private: | private: | ||||
| InterProcessLock& lock_; | InterProcessLock& lock_; | ||||
| bool lockWasSuccessful; | |||||
| ScopedLockType (const ScopedLockType&); | ScopedLockType (const ScopedLockType&); | ||||
| ScopedLockType& operator= (const ScopedLockType&); | ScopedLockType& operator= (const ScopedLockType&); | ||||
| @@ -10062,6 +10065,11 @@ public: | |||||
| return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); | return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); | ||||
| } | } | ||||
| const Rectangle operator- (const Point<ValueType>& deltaPosition) const throw() | |||||
| { | |||||
| return Rectangle (x - deltaPosition.getX(), y - deltaPosition.getY(), w, h); | |||||
| } | |||||
| void expand (const ValueType deltaX, | void expand (const ValueType deltaX, | ||||
| const ValueType deltaY) throw() | const ValueType deltaY) throw() | ||||
| { | { | ||||
| @@ -22888,6 +22896,8 @@ public: | |||||
| const Rectangle<int> resizeRectangleBy (Rectangle<int> original, | const Rectangle<int> resizeRectangleBy (Rectangle<int> original, | ||||
| const Point<int>& distance) const throw(); | const Point<int>& distance) const throw(); | ||||
| int getZoneFlags() const throw() { return zone; } | |||||
| private: | private: | ||||
| int zone; | int zone; | ||||
| @@ -99,13 +99,13 @@ const Rectangle<int> ResizableBorderComponent::Zone::resizeRectangleBy (Rectangl | |||||
| b.setLeft (b.getX() + offset.getX()); | b.setLeft (b.getX() + offset.getX()); | ||||
| if (isDraggingRightEdge()) | if (isDraggingRightEdge()) | ||||
| b.setWidth (b.getWidth() + offset.getX()); | |||||
| b.setWidth (jmax (0, b.getWidth() + offset.getX())); | |||||
| if (isDraggingTopEdge()) | if (isDraggingTopEdge()) | ||||
| b.setTop (b.getY() + offset.getY()); | b.setTop (b.getY() + offset.getY()); | ||||
| if (isDraggingBottomEdge()) | if (isDraggingBottomEdge()) | ||||
| b.setHeight (b.getHeight() + offset.getY()); | |||||
| b.setHeight (jmax (0, b.getHeight() + offset.getY())); | |||||
| return b; | return b; | ||||
| } | } | ||||
| @@ -141,6 +141,9 @@ public: | |||||
| const Rectangle<int> resizeRectangleBy (Rectangle<int> original, | const Rectangle<int> resizeRectangleBy (Rectangle<int> original, | ||||
| const Point<int>& distance) const throw(); | const Point<int>& distance) const throw(); | ||||
| /** Returns the raw flags for this zone. */ | |||||
| int getZoneFlags() const throw() { return zone; } | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| int zone; | int zone; | ||||
| @@ -221,6 +221,12 @@ public: | |||||
| return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); | return Rectangle (x + deltaPosition.getX(), y + deltaPosition.getY(), w, h); | ||||
| } | } | ||||
| /** Returns a rectangle which is the same as this one moved by a given amount. */ | |||||
| const Rectangle operator- (const Point<ValueType>& deltaPosition) const throw() | |||||
| { | |||||
| return Rectangle (x - deltaPosition.getX(), y - deltaPosition.getY(), w, h); | |||||
| } | |||||
| /** Expands the rectangle by a given amount. | /** Expands the rectangle by a given amount. | ||||
| Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). | Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). | ||||
| @@ -570,6 +570,9 @@ void InterProcessLock::exit() | |||||
| { | { | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| // Trying to release the lock too many times! | |||||
| jassert (pimpl != 0); | |||||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | if (pimpl != 0 && --(pimpl->refCount) == 0) | ||||
| pimpl = 0; | pimpl = 0; | ||||
| } | } | ||||
| @@ -418,6 +418,9 @@ void InterProcessLock::exit() | |||||
| { | { | ||||
| const ScopedLock sl (lock); | const ScopedLock sl (lock); | ||||
| // Trying to release the lock too many times! | |||||
| jassert (pimpl != 0); | |||||
| if (pimpl != 0 && --(pimpl->refCount) == 0) | if (pimpl != 0 && --(pimpl->refCount) == 0) | ||||
| pimpl = 0; | pimpl = 0; | ||||
| } | } | ||||
| @@ -86,11 +86,14 @@ public: | |||||
| when the ScopedLockType object is deleted, the InterProcessLock will | when the ScopedLockType object is deleted, the InterProcessLock will | ||||
| be unlocked. | be unlocked. | ||||
| Note that since an InterprocessLock can fail due to errors, you should check | |||||
| isLocked() to make sure that the lock was successful before using it. | |||||
| Make sure this object is created and deleted by the same thread, | Make sure this object is created and deleted by the same thread, | ||||
| otherwise there are no guarantees what will happen! Best just to use it | otherwise there are no guarantees what will happen! Best just to use it | ||||
| as a local stack object, rather than creating one with the new() operator. | as a local stack object, rather than creating one with the new() operator. | ||||
| */ | */ | ||||
| inline explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lock.enter(); } | |||||
| explicit ScopedLockType (InterProcessLock& lock) : lock_ (lock) { lockWasSuccessful = lock.enter(); } | |||||
| /** Destructor. | /** Destructor. | ||||
| @@ -101,9 +104,13 @@ public: | |||||
| */ | */ | ||||
| inline ~ScopedLockType() { lock_.exit(); } | inline ~ScopedLockType() { lock_.exit(); } | ||||
| /** Returns true if the InterProcessLock was successfully locked. */ | |||||
| bool isLocked() const throw() { return lockWasSuccessful; } | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| InterProcessLock& lock_; | InterProcessLock& lock_; | ||||
| bool lockWasSuccessful; | |||||
| ScopedLockType (const ScopedLockType&); | ScopedLockType (const ScopedLockType&); | ||||
| ScopedLockType& operator= (const ScopedLockType&); | ScopedLockType& operator= (const ScopedLockType&); | ||||
| @@ -86,10 +86,7 @@ public: | |||||
| */ | */ | ||||
| inline ~ScopedTryLock() throw() { if (lockWasSuccessful) lock_.exit(); } | inline ~ScopedTryLock() throw() { if (lockWasSuccessful) lock_.exit(); } | ||||
| /** Lock state | |||||
| @return True if the CriticalSection is locked. | |||||
| */ | |||||
| /** Returns true if the CriticalSection was successfully locked. */ | |||||
| bool isLocked() const throw() { return lockWasSuccessful; } | bool isLocked() const throw() { return lockWasSuccessful; } | ||||
| private: | private: | ||||